diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..e98f03d
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,32 @@
+The Yii framework is free software. It is released under the terms of
+the following BSD License.
+
+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 100644
index 0000000..45e56ad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,54 @@
+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).
+
+[](https://packagist.org/packages/yiisoft/yii2-app-advanced)
+[](https://packagist.org/packages/yiisoft/yii2-app-advanced)
+[](https://travis-ci.org/yiisoft/yii2-app-advanced)
+
+DIRECTORY STRUCTURE
+-------------------
+
+```
+common
+ config/ contains shared configurations
+ mail/ contains view files for e-mails
+ models/ contains model classes used in both backend and frontend
+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
+ 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
+ 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
+tests contains various tests for the advanced application
+ codeception/ contains tests developed with Codeception PHP Testing Framework
+```
diff --git a/backend/assets/AppAsset.php b/backend/assets/AppAsset.php
new file mode 100644
index 0000000..5b7678d
--- /dev/null
+++ b/backend/assets/AppAsset.php
@@ -0,0 +1,32 @@
+
+ * @since 2.0
+ */
+class AppAsset extends AssetBundle
+{
+ public $basePath = '@webroot';
+ public $baseUrl = '@web';
+ public $css = [
+ 'css/site.css',
+ 'css/flags32.css'
+ ];
+ public $js = [
+ 'js/option.js'
+ ];
+ public $depends = [
+ 'yii\web\YiiAsset',
+ 'yii\bootstrap\BootstrapAsset',
+ 'yii\web\JqueryAsset'
+ ];
+}
diff --git a/backend/config/.gitignore b/backend/config/.gitignore
new file mode 100644
index 0000000..20da318
--- /dev/null
+++ b/backend/config/.gitignore
@@ -0,0 +1,2 @@
+main-local.php
+params-local.php
\ No newline at end of file
diff --git a/backend/config/bootstrap.php b/backend/config/bootstrap.php
new file mode 100644
index 0000000..c81e9b4
--- /dev/null
+++ b/backend/config/bootstrap.php
@@ -0,0 +1,2 @@
+ 'app-backend',
+ 'basePath' => dirname(__DIR__),
+ 'controllerNamespace' => 'backend\controllers',
+ 'bootstrap' => ['log'],
+ 'modules' => [
+ 'permit' => [
+ 'class' => 'developeruz\db_rbac\Yii2DbRbac',
+ 'params' => [
+ 'userClass' => 'common\models\User'
+ ]
+ ],
+ ],
+ 'components' => [
+ 'user' => [
+ 'identityClass' => 'common\models\User',
+ 'enableAutoLogin' => true,
+ ],
+ 'urlManager' => [
+ 'enablePrettyUrl' => true,
+ 'showScriptName' => false,
+ ],
+ 'log' => [
+ 'traceLevel' => YII_DEBUG ? 3 : 0,
+ 'targets' => [
+ [
+ 'class' => 'yii\log\FileTarget',
+ 'levels' => ['error', 'warning'],
+ ],
+ ],
+ ],
+ 'errorHandler' => [
+ 'errorAction' => 'site/error',
+ ],
+ ],
+ 'params' => $params,
+];
diff --git a/backend/config/params.php b/backend/config/params.php
new file mode 100644
index 0000000..7f754b9
--- /dev/null
+++ b/backend/config/params.php
@@ -0,0 +1,4 @@
+ 'admin@example.com',
+];
diff --git a/backend/controllers/AdminMenuController.php b/backend/controllers/AdminMenuController.php
new file mode 100644
index 0000000..2e28a03
--- /dev/null
+++ b/backend/controllers/AdminMenuController.php
@@ -0,0 +1,123 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all AdminMenu models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new AdminMenuSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single AdminMenu model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new AdminMenu model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new AdminMenu();
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->admin_menu_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing AdminMenu model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->adminmenu_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing AdminMenu model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the AdminMenu model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return AdminMenu the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = AdminMenu::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+}
diff --git a/backend/controllers/BlogController.php b/backend/controllers/BlogController.php
new file mode 100644
index 0000000..5823d82
--- /dev/null
+++ b/backend/controllers/BlogController.php
@@ -0,0 +1,29 @@
+ Article::find(),
+ 'pagination' => [
+ 'pageSize' => 10
+ ]
+ ]);
+ return $this->render('articles', ['dataProvider' => $dataProvider]);
+ }
+
+}
\ No newline at end of file
diff --git a/backend/controllers/ImportController.php b/backend/controllers/ImportController.php
new file mode 100644
index 0000000..f37e584
--- /dev/null
+++ b/backend/controllers/ImportController.php
@@ -0,0 +1,38 @@
+render('index', [
+ 'model' => $model
+ ]);
+ }
+
+ public function actionUpload()
+ {
+ $model = new Import();
+
+ if ($model->load(Yii::$app->request->post()))
+ {
+ $model->file = UploadedFile::getInstances($model, 'file');
+
+ // Копируем файл в директорию
+ $path = $_SERVER['DOCUMENT_ROOT'].'/import/';
+
+ foreach ($model->file as $file)
+ {
+ //var_dump(substr ($path.$file->name, 0, -10)); die;
+ Import::importFile ($file);
+ }
+ }
+ }
+
+}
diff --git a/backend/controllers/LanguageController.php b/backend/controllers/LanguageController.php
new file mode 100644
index 0000000..92d8e9f
--- /dev/null
+++ b/backend/controllers/LanguageController.php
@@ -0,0 +1,190 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all Language models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new LanguageSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Creates a new Language model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ if(!empty(Yii::$app->request->get('id'))) {
+ $model = $this->findModel(Yii::$app->request->get('id'));
+ $model->status = 1;
+ $model->save();
+ return $this->redirect(['index']);
+ } else {
+ $searchModel = new LanguageSearch();
+ $dataProvider = $searchModel->searchNew(Yii::$app->request->queryParams);
+
+ return $this->render('create', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing Language model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $model = $this->findModel($id);
+ $model->status = 0;
+ $model->save();
+
+ return $this->redirect(['index']);
+ }
+
+ public function actionDefault($id)
+ {
+ $model = $this->findModel($id);
+ $models = Language::find()->where(['is_default' => 1])->all();
+ foreach($models as $onemodel) {
+ $onemodel->is_default = 0;
+ $onemodel->save();
+ }
+ $model->is_default = 1;
+ $model->save();
+ return $this->redirect(['index']);
+ }
+ /**
+ * Finds the Language model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Language the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = Language::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+ public function actionCreateAdress()
+ {
+ $form[0] = Option::create(\Yii::$app->request->post(), 'Main', 1, [['name' => 'adres', 'template' => 'text', 'translate' => true], ['name' => 'x', 'template' => 'number', 'translate' => false], ['name' => 'y', 'template' => 'number', 'translate' => false], ['name' => 'phone', 'template' => 'text', 'translate' => false], ['name' => 'name', 'template' => 'text', 'translate' => true]], true);
+ if($form[0]['success'] == false) {
+ return $this->render('create_adress', ['forms' => $form]);
+ } else {
+ return $this->redirect(['view-adress']);
+ }
+ }
+
+ public function actionUpdateAdress($id)
+ {
+ $form[0] = Option::change($id, \Yii::$app->request->post(), 'Main', 1);
+ if($form[0]['success'] == false) {
+ return $this->render('update_adress', ['forms' => $form]);
+ } else {
+ return $this->redirect(['view-adress']);
+ }
+ }
+
+ public function actionViewAdress()
+ {
+ $searchModel = new OptionSearch();
+
+ $dataProvider = $searchModel->search(array_merge(Yii::$app->request->queryParams, ['OptionSearch' => ['model' => 'Main', 'name' => 'adres']]));
+
+ return $this->render('view_adress', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Deletes an existing Option model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDeleteAdress($id)
+ {
+ $model = $this->findModelAdress($id);
+ $children = $model->hasMany(Option::className(), ['option_pid' => 'option_id'])->all();
+ $langs = array();
+ if(!empty($children)) {
+ foreach($children as $child) {
+ $langs = OptionLang::findAll(['option_language_id' => $child->option_id]);
+ foreach($langs as $lang) {
+ $lang->delete();
+ }
+ $child->delete();
+ }
+ }
+ $langs = OptionLang::findAll(['option_language_id' => $id]);
+ foreach($langs as $lang) {
+ $lang->delete();
+ }
+ $model->delete();
+
+ return $this->redirect(['view-adress']);
+ }
+
+ /**
+ * Finds the Option model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Option the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModelAdress($id)
+ {
+ if (($model = Option::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/backend/controllers/MenuController.php b/backend/controllers/MenuController.php
new file mode 100644
index 0000000..cfa24a2
--- /dev/null
+++ b/backend/controllers/MenuController.php
@@ -0,0 +1,122 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all Menu models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new MenuSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single Menu model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new Menu model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new Menu();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->menu_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing Menu model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->menu_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing Menu model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the Menu model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Menu the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = Menu::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+}
diff --git a/backend/controllers/MenuLocationController.php b/backend/controllers/MenuLocationController.php
new file mode 100644
index 0000000..68fdf69
--- /dev/null
+++ b/backend/controllers/MenuLocationController.php
@@ -0,0 +1,121 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all MenuLocation models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new MenuLocationSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single MenuLocation model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new MenuLocation model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new MenuLocation();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->menu_location_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing MenuLocation model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->menu_location_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing MenuLocation model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the MenuLocation model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return MenuLocation the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = MenuLocation::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/backend/controllers/NewOptionsLangController.php b/backend/controllers/NewOptionsLangController.php
new file mode 100644
index 0000000..e2bab3e
--- /dev/null
+++ b/backend/controllers/NewOptionsLangController.php
@@ -0,0 +1,121 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all NewOptionsLang models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new NewOptionsLangSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single NewOptionsLang model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new NewOptionsLang model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new NewOptionsLang();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->primary]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing NewOptionsLang model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->primary]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing NewOptionsLang model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the NewOptionsLang model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return NewOptionsLang the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = NewOptionsLang::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/backend/controllers/OptionController.php b/backend/controllers/OptionController.php
new file mode 100644
index 0000000..1817d35
--- /dev/null
+++ b/backend/controllers/OptionController.php
@@ -0,0 +1,132 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all Option models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new OptionSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single Option model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new Option model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $form[0] = Option::create(\Yii::$app->request->post(), 'User', 10, [['name' => 'phone', 'template' => 'text', 'translate' => true], ['name' => 'adres', 'template' => 'text', 'translate' => false]]);
+ if($form[0]['success'] == false) {
+ return $this->render('create', ['forms' => $form]);
+ } else {
+ return $this->redirect(['index']);
+ }
+ }
+
+ /**
+ * Updates an existing Option model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $form[0] = Option::change($id, \Yii::$app->request->post(), 'User', 10);
+ if($form[0]['success'] == false) {
+ return $this->render('update', ['forms' => $form]);
+ } else {
+ return $this->redirect(['view', 'id' => $id]);
+ }
+ }
+
+ /**
+ * Deletes an existing Option model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $model = $this->findModel($id);
+ $children = $model->hasMany(Option::className(), ['parent_id' => 'option_id'])->all();
+ $langs = array();
+ if(!empty($children)) {
+ foreach($children as $child) {
+ $langs = OptionLang::findAll(['id' => $child->option_id]);
+ foreach($langs as $lang) {
+ $lang->delete();
+ }
+ $child->delete();
+ }
+ }
+ $langs = OptionLang::findAll(['id' => $id]);
+ foreach($langs as $lang) {
+ $lang->delete();
+ }
+ $model->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the Option model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Option the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = Option::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/backend/controllers/SettingsController.php b/backend/controllers/SettingsController.php
new file mode 100644
index 0000000..31de354
--- /dev/null
+++ b/backend/controllers/SettingsController.php
@@ -0,0 +1,38 @@
+ [
+ 'class' => 'yii\web\ErrorAction',
+ ],
+ ];
+ }
+
+ public function actionIndex()
+ {
+ return $this->render('index');
+ }
+
+}
diff --git a/backend/controllers/SiteController.php b/backend/controllers/SiteController.php
new file mode 100644
index 0000000..5a723bd
--- /dev/null
+++ b/backend/controllers/SiteController.php
@@ -0,0 +1,158 @@
+ [
+ 'class' => AccessControl::className(),
+ 'except' => ['login', 'error'],
+ 'rules' => [
+ [
+ 'allow' => true,
+ 'roles' => ['@']
+ ],
+ ],
+ ],
+ 'verbs' => [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'logout' => ['post'],
+ 'delete-req' => ['post']
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function actions()
+ {
+ return [
+ 'error' => [
+ 'class' => 'yii\web\ErrorAction',
+ ],
+ ];
+ }
+
+ public function actionIndex()
+ {
+/*
+ if (Yii::$app->user->can('site/index'))
+ {
+
+ }
+ else
+ {
+ Yii::$app->getSession()->setFlash('error', 'Доступ закрыт..');
+
+ return $this->redirect('/');
+ }
+*/
+ return $this->render('index');
+ }
+
+ public function actionLogin()
+ {
+ if (!\Yii::$app->user->isGuest) {
+ return $this->goHome();
+ }
+
+ $model = new LoginForm();
+ if ($model->load(Yii::$app->request->post()) && $model->login()) {
+ return $this->goBack();
+ } else {
+ return $this->render('login', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ public function actionLogout()
+ {
+ Yii::$app->user->logout();
+
+ return $this->goHome();
+ }
+
+
+ /**
+ * Displays Profile page.
+ *
+ * @return mixed
+ */
+ public function actionProfile()
+ {
+ $model = new Profile();
+
+ $model->saveProfile();
+
+ return $this->render('profile', [
+ 'model' => $model,
+ ]);
+ }
+
+ public function actionRequests($id = 0) {
+ if($id != 0) {
+ Option::markOld($id);
+ $this->redirect(['site/requests']);
+ }
+ $query = Option::find()->where(['model' => 'Feedback', 'model_id' => 1, 'parent_id' => null]);
+ $provider = new ActiveDataProvider([
+ 'query' => $query,
+ 'pagination' => [
+ 'pageSize' => 2,
+ ],
+ 'sort' => [
+ 'defaultOrder' => [
+ 'created_at' => SORT_DESC
+ ]
+ ]
+ ]);
+ $data = $provider->getModels();
+ return $this->render('requests', [
+ 'dataProvider' => $provider
+ ]);
+ }
+
+ public function actionDeleteReq($id) {
+ $model = Option::findOne($id);
+ if(!is_null($model) && $model->delete()) {
+ return $this->redirect(['site/requests']);
+ } else{
+ Yii::$app->session->setFlash('post_error', $model->getFirstErrors());
+ return $this->redirect('[site/requests]');
+ }
+ }
+
+ public function actionTest()
+ {
+ echo "
";
+ //var_dump(Yii::$app->getAuthManager()->getRole('CHUVAK'));
+ //var_dump(Yii::$app->getAuthManager()->assign(Yii::$app->getAuthManager()->getRole('CHUVAK'), Yii::$app->user->getId()));
+ var_dump(Yii::$app->getAuthManager()->getRoles());
+ echo " ";
+ return $this->render('index');
+ }
+
+}
diff --git a/backend/controllers/TerminController.php b/backend/controllers/TerminController.php
new file mode 100644
index 0000000..e4774ae
--- /dev/null
+++ b/backend/controllers/TerminController.php
@@ -0,0 +1,162 @@
+ [
+ 'class' => AccessControl::className(),
+ 'except' => ['login', 'error', 'index', 'create', 'update'],
+ 'rules' => [
+ [
+ 'allow' => true,
+ 'roles' => ['admin']
+ ],
+ ],
+ ],
+ 'verbs' => [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'logout' => ['post'],
+ 'delete-req' => ['post']
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all Termin models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new TerminSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single Termin model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new Termin model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new Termin();
+ $model_lang = new TerminLang();
+
+ if ($model->load(Yii::$app->request->post())
+ && $model_lang->load(Yii::$app->request->post()))
+ {
+ $model->save();
+ $model_lang->termin_id = $model->termin_id;
+ $model_lang->save();
+
+ return $this->redirect(['view', 'id' => $model->termin_id]);
+ }
+ else
+ {
+ return $this->render('create', [
+ 'model' => $model,
+ 'model_lang' => $model_lang,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing Termin model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+ $model_lang = TerminLang::findOne($id);
+ $model_pid = TerminStructure::findOne($id)->getRelation('terminPid')->one();
+
+ //var_dump(Yii::$app->request->post());
+ //var_dump($model_pid->termin->termin_id); die;
+
+ if ($model->load(Yii::$app->request->post())
+ && $model_lang->load(Yii::$app->request->post())
+ && $model_pid->load(Yii::$app->request->post())
+ )
+ {
+ $model->save();
+ $model_lang->save();
+ $model_pid->termin_pid = $model_pid->termin->termin_id;
+
+ return $this->redirect(['view', 'id' => $model->termin_id]);
+ }
+ else
+ {
+ return $this->render('update', [
+ 'model' => $model,
+ 'model_lang' => $model_lang,
+ 'model_pid' => $model_pid,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing Termin model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the Termin model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Termin the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = Termin::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/backend/controllers/UserController.php b/backend/controllers/UserController.php
new file mode 100644
index 0000000..61c0ee4
--- /dev/null
+++ b/backend/controllers/UserController.php
@@ -0,0 +1,121 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all User models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new UserSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single User model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new User model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new User();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing User model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing User model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the User model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return User the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = User::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/backend/models/.gitkeep b/backend/models/.gitkeep
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/backend/models/.gitkeep
@@ -0,0 +1 @@
+*
diff --git a/backend/models/AdminMenu.php b/backend/models/AdminMenu.php
new file mode 100644
index 0000000..2dd3ce1
--- /dev/null
+++ b/backend/models/AdminMenu.php
@@ -0,0 +1,152 @@
+ Yii::t('app', 'Admin menu ID'),
+ 'admin_menu_pid' => Yii::t('app', 'Admin menu parent ID'),
+ 'status' => Yii::t('app', 'Status'),
+ 'hide_min' => Yii::t('app', 'Hide Min'),
+ 'sort' => Yii::t('app', 'Sort'),
+ 'name' => Yii::t('app', 'Name'),
+ 'path' => Yii::t('app', 'Path'),
+ 'param' => Yii::t('app', 'Params'),
+ 'parentt' => Yii::t('app', 'Parent item')
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getParent()
+ {
+ return $this->hasOne(AdminMenu::className(), ['admin_menu_id' => 'admin_menu_pid']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getAdminMenus()
+ {
+ return $this->hasMany(AdminMenu::className(), ['admin_menu_pid' => 'admin_menu_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getAdminMenuAccessGroups()
+ {
+ return $this->hasMany(AdminMenuAccessGroup::className(), ['admin_menu_id' => 'admin_menu_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getAdminMenuAccessUsers()
+ {
+ return $this->hasMany(AdminMenuAccessUser::className(), ['admin_menu_id' => 'admin_menu_id']);
+ }
+
+ public static function buildMenu($withValues = false)
+ {
+ $result = [];
+ $roots = self::find()->where(['admin_menu_pid' => NULL])->with(['adminMenus'])->all();
+ foreach($roots as $root) {
+ if($root->adminMenus) {
+ $result[] = ['label' => Yii::t('app', $root->name), 'id' => $root->admin_menu_id, 'options' => ['class' => 'header']];
+ foreach($root->adminMenus as $submenu) {
+ if($submenu->adminMenus) {
+ $items = [];
+ foreach($submenu->adminMenus as $item) {
+ $items[] = ['label' => Yii::t('app', $item->name), 'id' => $item->admin_menu_id, 'icon' => 'fa fa-circle-o', 'url' => array_merge([$item->path], \common\models\Tools::parseUrlParams($item->param))];
+ }
+ $result[] = ['label' => Yii::t('app', $submenu->name), 'id' => $submenu->admin_menu_id, 'icon' => 'fa fa-circle-o', 'url' => '#', 'items' => $items];
+ unset($items);
+ } else {
+ $result[] = ['label' => Yii::t('app', $submenu->name), 'id' => $submenu->admin_menu_id, 'icon' => 'fa fa-circle-o', 'url' => array_merge([$submenu->path], \common\models\Tools::parseUrlParams($submenu->param))];
+ }
+ }
+ }
+ }
+ return $result;
+ }
+
+ public static function buildMenuSelect()
+ {
+ $result = [];
+ $roots = self::find()->where(['admin_menu_pid' => NULL])->with(['adminMenus'])->all();
+ foreach($roots as $root) {
+ if($root->adminMenus) {
+ $items = [];
+ foreach($root->adminMenus as $submenu) {
+ $items[] = ['label' => Yii::t('app', $submenu->name), 'id' => $submenu->admin_menu_id];
+ }
+ $result[] = ['label' => Yii::t('app', $root->name), 'id' => $root->admin_menu_id, 'items' => $items];
+ unset($items);
+ } else {
+ $result[] = ['label' => Yii::t('app', $root->name), 'id' => $root->admin_menu_id];
+ }
+ }
+ return $result;
+ }
+
+ public function beforeSave($insert)
+ {
+ if (parent::beforeSave($insert)) {
+ if(!$this->admin_menu_pid) {
+ $this->admin_menu_pid = NULL;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/backend/models/AdminMenuAccessGroup.php b/backend/models/AdminMenuAccessGroup.php
new file mode 100644
index 0000000..5b6a266
--- /dev/null
+++ b/backend/models/AdminMenuAccessGroup.php
@@ -0,0 +1,65 @@
+ Yii::t('app', 'Admin Access Group ID'),
+ 'admin_menu_id' => Yii::t('app', 'Admin Menu ID'),
+ 'group' => Yii::t('app', 'Group'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getMenu()
+ {
+ return $this->hasOne(AdminMenu::className(), ['admin_menu_id' => 'admin_menu_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getGroup0()
+ {
+ return $this->hasOne(AuthRule::className(), ['name' => 'group']);
+ }
+}
diff --git a/backend/models/AdminMenuAccessUser.php b/backend/models/AdminMenuAccessUser.php
new file mode 100644
index 0000000..94a0833
--- /dev/null
+++ b/backend/models/AdminMenuAccessUser.php
@@ -0,0 +1,65 @@
+ Yii::t('app', 'Admin Menu ID'),
+ 'user_id' => Yii::t('app', 'User ID'),
+ 'admin_menu_access_user_id' => Yii::t('app', 'Admin Menu Access User ID'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getMenu()
+ {
+ return $this->hasOne(AdminMenu::className(), ['admin_menu_id' => 'admin_menu_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getUser()
+ {
+ return $this->hasOne(User::className(), ['id' => 'user_id']);
+ }
+}
diff --git a/backend/models/AdminMenuSearch.php b/backend/models/AdminMenuSearch.php
new file mode 100644
index 0000000..0ad3652
--- /dev/null
+++ b/backend/models/AdminMenuSearch.php
@@ -0,0 +1,94 @@
+ $query,
+ 'pagination' => [
+ 'pageSize' => 5
+ ],
+ 'sort' => [
+ 'attributes' => [
+ 'admin_menu_id',
+ 'name',
+ 'path',
+ 'param',
+ 'status',
+ 'hide_min',
+ 'parentt' => [
+ 'asc' => ['name' => SORT_ASC],
+ 'desc' => ['name' => SORT_DESC],
+ 'default' => SORT_DESC
+ ]
+ ]
+ ]
+ ]);
+
+ $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;
+ }
+
+ $query->andFilterWhere([
+ 'admin_menu_id' => $this->admin_menu_id,
+ 'admin_menu_pid' => $this->admin_menu_pid,
+ 'status' => $this->status,
+ 'hide_min' => $this->hide_min,
+ 'sort' => $this->sort
+ ]);
+
+
+ $query->andFilterWhere(['like', 'name', $this->name])
+ ->andFilterWhere(['like', 'path', $this->path])
+ ->andFilterWhere(['like', 'param', $this->param])
+ ->andFilterWhere(['in', 'admin_menu_pid', $this->find()->select(['admin_menu_id'])->andFilterWhere(['like', 'name', $this->parentt])->column()]);
+
+ return $dataProvider;
+ }
+}
diff --git a/backend/models/Import.php b/backend/models/Import.php
new file mode 100644
index 0000000..a159d06
--- /dev/null
+++ b/backend/models/Import.php
@@ -0,0 +1,184 @@
+ false, 'extensions' => 'csv', 'maxFiles' => 2],
+ ];
+ }
+
+ static function importFile ($filename)
+ {
+ // создаем папку
+ if (! is_dir ($path = $_SERVER['DOCUMENT_ROOT'].'/import/'))
+ {
+ mkdir ($path, 0777, true);
+ }
+
+ // копируем файл
+ copy ($filename->tempName, $path.$filename->name);
+
+ // по умолчанию
+ // $termin_pid MEGA КАСТЫЛЬ!!! Это категория "Каталог товаров",
+ // под которую подтягуються термины, которые на нашли себе parent
+ $termin_pid = 8;
+ // $template_id шаблон каьегорий
+ $template_id = 3;
+ $language_id = 2;
+ $type = 'H';
+
+ // массив для импортп товаров
+ $MASS = [];
+
+ // открываем файл и перебираем
+ $fp = fopen ($path.$filename->name, 'r');
+ while ($ROW = fgetcsv ($fp, 10000, ';'))
+ {
+ // чистим
+ foreach ($ROW as $key => &$value)
+ {
+ $value = trim ($value) == 'NULL' ? NULL : trim ($value);
+ }
+
+ $ROW['category_title'] = $ROW[0];
+ $ROW['group_title'] = $ROW[1];
+ $ROW['subgroup_title'] = $ROW[2];
+
+ // проверяем если через ","
+
+ // var_dump($array[1]); die;
+
+ // массив для поиска/добавления термина
+ $basic = [
+ 'type' => $type,
+ 'language_id' => $language_id,
+ 'template_id' => $template_id,
+ ];
+
+ // категория
+ if ($ROW['category_title'] == NULL)
+ {
+ CONTINUE;
+ }
+
+ $termin_id = Termin::addIfNotExists ($basic + [
+ 'termin_title' => $ROW['category_title'],
+ 'termin_pid' => $termin_pid, // MEGA КАСТЫЛЬ!!!
+ ]);
+
+ // подгруппа
+ if ($ROW['group_title'] != NULL)
+ {
+ $termin_id = Termin::addIfNotExists ($basic + [
+ 'termin_title' => $ROW['group_title'],
+ 'termin_pid' => $termin_id,
+ ]);
+ }
+
+ // группа
+ if ($ROW['subgroup_title'] != NULL)
+ {
+ $termin_id = Termin::addIfNotExists ($basic + [
+ 'termin_title' => $ROW['subgroup_title'],
+ 'termin_pid' => $termin_id,
+ ]);
+ }
+
+ }
+
+ // удаляем файл
+ chmod($path.$filename->name, 0777);
+ unlink ($path.$filename->name);
+
+/*
+ echo '';
+
+ var_dump($category);
+ var_dump($group);
+ var_dump($subgroup);
+
+ echo ' ';
+
+ // ОБЩЕЕ
+ // PRODUCT
+ Артикул
+ Категория
+ Группа
+ Подгруппа
+ Описание
+ Штрих-код
+
+ // СПРАВОЧНИК ИЛИ ДОП. ПОЛЯ
+ // ???
+ Торговая марка
+ Производитель
+ ID
+ Наименование
+ кол-во в пакете
+ Ед. Изм
+ опт / розница
+ Диаметр шляпки min
+ Диаметр шляпки max
+ Единица измерения
+ Цвет шляпки
+ Цвет шляпки (фильтр)
+ Цвет мякоти цвет мякоти (фильтр)
+ Длина ножки min
+ Длина ножки max
+ Единица измерения
+ примечание
+*/
+ }
+
+ public function findTerminOneQuery ($type, $name, $parent_name)
+ {
+/*
+ return yii::$app->db->createCommand('
+ SELECT
+ `termin`.termin_id, `parent`.termin_id as termin_pid
+ FROM `termin`
+ INNER JOIN `termin_lang` ON `termin_lang`.termin_alias = "'.$name.'"
+ INNER JOIN `termin_relation` ON `termin_relation`.termin_id = `termin`.termin_id
+ LEFT JOIN (
+ IF NOT EXISTS (SELECT *
+ FROM `termin_lang`
+ INNER JOIN `termin` ON `termin`.termin_id = `termin_lang`.termin_id
+ AND `termin`.type = "'.$type.'"
+ WHERE `termin_lang`.termin_alias = "'.$parent_name.'"
+ )
+ THEN (SELECT *
+ FROM `termin_lang`
+ INNER JOIN `termin` ON `termin`.termin_id = `termin_lang`.termin_id
+ AND `termin`.type = "'.$type.'"
+ WHERE `termin_lang`.termin_alias = "'.$parent_name.'"
+ )
+ ELSE (SELECT *
+ FROM `termin_lang`
+ INNER JOIN `termin` ON `termin`.termin_id = `termin_lang`.termin_id
+ AND `termin`.type = "'.$type.'"
+ )
+ ) as `parent` ON `parent`.termin_id = `termin_relation`.termin_pid
+ WHERE `termin`.type = "'.$type.'"
+ ')->queryOne();
+*/
+ }
+
+}
diff --git a/backend/models/Language.php b/backend/models/Language.php
new file mode 100644
index 0000000..9b8e3ae
--- /dev/null
+++ b/backend/models/Language.php
@@ -0,0 +1,52 @@
+ 4],
+ [['language_name'], 'string', 'max' => 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'language_id' => Yii::t('app', 'Language ID'),
+ 'lang_code' => Yii::t('app', 'Lang Code'),
+ 'is_default' => Yii::t('app', 'Is Default'),
+ 'language_name' => Yii::t('app', 'Language Name'),
+ 'active' => Yii::t('app', 'Active'),
+ ];
+ }
+}
diff --git a/backend/models/LanguageSearch.php b/backend/models/LanguageSearch.php
new file mode 100644
index 0000000..3a1610e
--- /dev/null
+++ b/backend/models/LanguageSearch.php
@@ -0,0 +1,101 @@
+ $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;
+ }
+
+ $query->andFilterWhere([
+ 'language_id' => $this->language_id,
+ 'is_default' => $this->is_default,
+ 'status' => $this->status,
+ ]);
+
+ $query->andFilterWhere(['like', 'language_code', $this->language_code])
+ ->andFilterWhere(['like', 'language_name', $this->language_name])
+ ->andWhere(['status' => '1'])
+ ->andWhere(['>', 'language_id', '0']);
+
+ return $dataProvider;
+ }
+
+ public function searchNew($params)
+ {
+ $query = Language::find();
+
+ $dataProvider = new ActiveDataProvider([
+ 'query' => $query,
+ ]);
+
+ $this->load($params);
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ $query->andFilterWhere([
+ 'language_id' => $this->language_id,
+ 'is_default' => $this->is_default,
+ 'status' => $this->status,
+ ]);
+
+ $query->andFilterWhere(['like', 'language_code', $this->language_code])
+ ->andFilterWhere(['like', 'language_name', $this->language_name])
+ ->andWhere(['status' => '0']);
+
+ return $dataProvider;
+ }
+}
diff --git a/backend/models/Menu.php b/backend/models/Menu.php
new file mode 100644
index 0000000..e2bb8c1
--- /dev/null
+++ b/backend/models/Menu.php
@@ -0,0 +1,102 @@
+ 250]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'menu_id' => Yii::t('app', 'Menu ID'),
+ 'menu_pid' => Yii::t('app', 'Menu Pid'),
+ 'level' => Yii::t('app', 'Level'),
+ 'termin_id' => Yii::t('app', 'Termin ID'),
+ 'status' => Yii::t('app', 'Show'),
+ 'is_open' => Yii::t('app', 'Is Open'),
+ 'menu_location_id' => Yii::t('app', 'Menu Location ID'),
+ 'sort' => Yii::t('app', 'Sortorder'),
+ 'name' => Yii::t('app', 'Name'),
+ 'url' => Yii::t('app', 'Url'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTermin()
+ {
+ return $this->hasOne(Termin::className(), ['termin_id' => 'termin_id']);
+ }
+
+ public function getMenuList ($location_name)
+ {
+ return yii::$app->db->createCommand('
+ SELECT
+ menu.menu_id, menu.menu_pid, menu.level,
+ termin_lang.termin_title, termin_lang.termin_alias
+ FROM menu
+ INNER JOIN menu_location ON menu_location.menu_location_id = menu.menu_location_id
+ AND menu_location.menu_location_name = \''.$location_name.'\'
+ INNER JOIN termin ON termin.termin_id = menu.termin_id
+ INNER JOIN termin_lang ON termin_lang.termin_id = menu.termin_id
+ AND termin_lang.language_id = '.Yii::$app->params['language_id'].'
+ ORDER BY menu.level ASC, menu.sort ASC
+ ')->queryAll();
+/*
+ return $this->find()
+ ->selectOption('termin_lang.termin_title')
+ ->from('menu')
+ ->join(
+ 'INNER JOIN',
+ 'termin_lang.termin_id = menu.termin_id',
+ ['language_id' => yii::$app->params['language_id']])
+ ->all();
+ */
+ }
+
+ public function getTerminLang()
+ {
+ return $this->hasOne(TerminLang::className(), ['termin_id' => 'termin_id']);
+ }
+}
diff --git a/backend/models/MenuLocation.php b/backend/models/MenuLocation.php
new file mode 100644
index 0000000..e554021
--- /dev/null
+++ b/backend/models/MenuLocation.php
@@ -0,0 +1,64 @@
+ 250]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'menu_location_id' => Yii::t('app', 'Menu Location ID'),
+ 'menu_location_name' => Yii::t('app', 'Menu Location Name'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getMenuLocationLangs()
+ {
+ return $this->hasMany(MenuLocationLang::className(), ['menu_location_id' => 'menu_location_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getLangs()
+ {
+ return $this->hasMany(Language::className(), ['language_id' => 'language_id'])->viaTable('menu_location_lang', ['menu_location_id' => 'menu_location_id']);
+ }
+}
diff --git a/backend/models/MenuLocationLang.php b/backend/models/MenuLocationLang.php
new file mode 100644
index 0000000..273d4d1
--- /dev/null
+++ b/backend/models/MenuLocationLang.php
@@ -0,0 +1,66 @@
+ 250]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'menu_location_id' => Yii::t('app', 'Menu Location ID'),
+ 'menu_location_title' => Yii::t('app', 'Menu Location Title'),
+ 'language_id' => Yii::t('app', 'Lang ID'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getLang()
+ {
+ return $this->hasOne(Language::className(), ['language_id' => 'language_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getMenuLocation()
+ {
+ return $this->hasOne(MenuLocation::className(), ['menu_location_id' => 'menu_location_id']);
+ }
+}
diff --git a/backend/models/MenuLocationSearch.php b/backend/models/MenuLocationSearch.php
new file mode 100644
index 0000000..a7d42e0
--- /dev/null
+++ b/backend/models/MenuLocationSearch.php
@@ -0,0 +1,66 @@
+ $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;
+ }
+
+ $query->andFilterWhere([
+ 'menu_location_id' => $this->menu_location_id,
+ ]);
+
+ $query->andFilterWhere(['like', 'menu_location_name', $this->menu_location_name]);
+
+ return $dataProvider;
+ }
+}
diff --git a/backend/models/MenuSearch.php b/backend/models/MenuSearch.php
new file mode 100644
index 0000000..6efaa39
--- /dev/null
+++ b/backend/models/MenuSearch.php
@@ -0,0 +1,75 @@
+ $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;
+ }
+
+ $query->andFilterWhere([
+ 'menu_id' => $this->menu_id,
+ 'menu_pid' => $this->menu_pid,
+ 'level' => $this->level,
+ 'termin_id' => $this->termin_id,
+ 'status' => $this->status,
+ 'is_open' => $this->is_open,
+ 'menu_location_id' => $this->menu_location_id,
+ 'sort' => $this->sort,
+ ]);
+
+ $query->andFilterWhere(['like', 'name', $this->name])
+ ->andFilterWhere(['like', 'url', $this->url]);
+
+ return $dataProvider;
+ }
+}
diff --git a/backend/models/NewOptions.php b/backend/models/NewOptions.php
new file mode 100644
index 0000000..2f7abbb
--- /dev/null
+++ b/backend/models/NewOptions.php
@@ -0,0 +1,53 @@
+ 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'model' => Yii::t('app', 'Model'),
+ 'model_id' => Yii::t('app', 'Model ID'),
+ 'name' => Yii::t('app', 'Name'),
+ 'template' => Yii::t('app', 'Template'),
+ 'parent_id' => Yii::t('app', 'Parent ID'),
+ ];
+ }
+}
diff --git a/backend/models/NewOptionsLang.php b/backend/models/NewOptionsLang.php
new file mode 100644
index 0000000..d4b0826
--- /dev/null
+++ b/backend/models/NewOptionsLang.php
@@ -0,0 +1,52 @@
+ Yii::t('app', 'Primary'),
+ 'id' => Yii::t('app', 'ID'),
+ 'language_id' => Yii::t('app', 'Lang ID'),
+ 'value' => Yii::t('app', 'Value'),
+ ];
+ }
+}
diff --git a/backend/models/NewOptionsLangSearch.php b/backend/models/NewOptionsLangSearch.php
new file mode 100644
index 0000000..e91eca3
--- /dev/null
+++ b/backend/models/NewOptionsLangSearch.php
@@ -0,0 +1,68 @@
+ $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;
+ }
+
+ $query->andFilterWhere([
+ 'primary' => $this->primary,
+ 'id' => $this->id,
+ 'language_id' => $this->language_id,
+ ]);
+
+ $query->andFilterWhere(['like', 'value', $this->value]);
+
+ return $dataProvider;
+ }
+}
diff --git a/backend/models/Profile.php b/backend/models/Profile.php
new file mode 100644
index 0000000..3888d96
--- /dev/null
+++ b/backend/models/Profile.php
@@ -0,0 +1,63 @@
+lastname = \Yii::$app->user->identity->lastname;
+ $this->firstname = \Yii::$app->user->identity->firstname;
+ $this->middlename = \Yii::$app->user->identity->middlename;
+ $this->username = \Yii::$app->user->identity->username;
+ $this->email = \Yii::$app->user->identity->email;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ // name, email, subject and body are required
+ [['firstname', 'lastname', 'email', 'password', 'middlename', 'username'], 'required'],
+ // email has to be a valid email address
+ ['email', 'email'],
+ ];
+ }
+
+ public function saveProfile()
+ {
+ if ($this->load(Yii::$app->request->post()))
+ {
+ $user = User::find()->where(['id' => Yii::$app->user->identity->id])->one();
+
+ $user->lastname = $this->lastname;
+ $user->firstname = $this->firstname;
+ $user->middlename = $this->middlename;
+ $user->username = $this->username;
+ $user->email = $this->email;
+ $user->password_hash = Yii::$app->security->generatePasswordHash($this->password);
+
+ $user->save();
+ }
+ }
+
+}
diff --git a/backend/models/Termin.php b/backend/models/Termin.php
new file mode 100644
index 0000000..af65884
--- /dev/null
+++ b/backend/models/Termin.php
@@ -0,0 +1,184 @@
+ 250],
+ [['termin_name'], 'string', 'max' => 250]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'termin_id' => Yii::t('app', 'termin'),
+ 'termin_name' => Yii::t('app', 'name').' (SYSTEM NAME)',
+ 'is_book' => Yii::t('app', 'book'),
+ ];
+ }
+
+ /**
+ * Выполняет поиск по параметрам
+ * @param array $param принимает [termin_id, language_id, return_one, return_field, show_all]
+ * @return array one | array all | string значение масива
+ */
+ public function finInfo (array $params = [])
+ {
+ Tools::ifNotExist ($params, array (
+ 'termin_id' => false,
+ 'termin_pid' => false,
+ 'language_id' => Yii::$app->params['language_id'],
+ 'return_one' => false,
+ 'return_field' => false,
+ 'show_all' => false,
+ 'to_array' => true,
+ 'pid_title' => false,
+ ));
+
+ $WHERE = $SELECT = array ();
+
+ $model = new self();
+
+ $SELECT[] = 'termin.*';
+ $query = $model->find();
+
+ if ($params['termin_id'])
+ {
+ $WHERE['termin.termin_id'] = $params['termin_id'];
+ }
+
+ // перевод
+ $SELECT[] = 'termin_lang.*';
+ $query->join(
+ 'INNER JOIN', 'termin_lang',
+ 'termin.termin_id = termin_lang.termin_id'
+ );
+
+ if ($params['language_id'])
+ {
+ $WHERE['termin_lang.language_id'] = $params['language_id'];
+ }
+
+ // структура
+ $SELECT[] = 'termin_structure.*';
+ $query->join(
+ 'INNER JOIN', 'termin_structure',
+ 'termin.termin_id = termin_structure.termin_id'
+ );
+
+ if ($params['termin_pid'] !== false)
+ {
+ $WHERE['termin_structure.termin_pid'] = $params['termin_pid'];
+ }
+
+ if ($params['pid_title'])
+ {
+ $SELECT[] = 'termin_pid_lang.termin_title as termin_pid_title';
+ $query->join(
+ 'LEFT JOIN', 'termin_lang as termin_pid_lang',
+ 'termin_pid_lang.termin_id = termin_structure.termin_pid'
+ );
+ }
+
+ // SELECT
+ if (! empty ($SELECT))
+ {
+ $query->select($SELECT);
+ }
+
+ // WHERE
+ if (! empty ($WHERE))
+ {
+ $query->where($WHERE);
+ }
+
+ if ($params['to_array'])
+ {
+ $query = $query->asArray();
+ }
+
+ if ($params['return_one'] || $params['return_field'])
+ {
+
+ $result = $params['return_field'] ? $query->one($params['return_field']) : $query->one();
+ }
+ else
+ {
+ $result = $query->all();
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getMenus()
+ {
+ return $this->hasMany(Menu::className(), ['termin_id' => 'termin_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTerminLangs()
+ {
+ return $this->hasMany(TerminLang::className(), ['termin_id' => 'termin_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getLangs()
+ {
+ return $this->hasMany(Language::className(), ['language_id' => 'language_id'])
+ ->viaTable('termin_lang', ['termin_id' => 'termin_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTerminStructures()
+ {
+ return $this->hasMany(TerminStructure::className(), ['termin_id' => 'termin_id']);
+ }
+
+}
diff --git a/backend/models/TerminLang.php b/backend/models/TerminLang.php
new file mode 100644
index 0000000..487701e
--- /dev/null
+++ b/backend/models/TerminLang.php
@@ -0,0 +1,68 @@
+ 250]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'termin_id' => Yii::t('app', 'Termin ID'),
+ 'language_id' => Yii::t('app', 'Lang ID'),
+ 'termin_title' => Yii::t('app', 'Termin Title'),
+ 'termin_alias' => Yii::t('app', 'Termin Alias'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getLang()
+ {
+ return $this->hasOne(Language::className(), ['language_id' => 'language_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTermin()
+ {
+ return $this->hasOne(Termin::className(), ['termin_id' => 'termin_id']);
+ }
+}
diff --git a/backend/models/TerminRelation.php b/backend/models/TerminRelation.php
new file mode 100644
index 0000000..8971a80
--- /dev/null
+++ b/backend/models/TerminRelation.php
@@ -0,0 +1,43 @@
+ Yii::t('app', 'Termin ID'),
+ 'termin_id_related' => Yii::t('app', 'Termin Id Related'),
+ ];
+ }
+}
diff --git a/backend/models/TerminSearch.php b/backend/models/TerminSearch.php
new file mode 100644
index 0000000..71f46d3
--- /dev/null
+++ b/backend/models/TerminSearch.php
@@ -0,0 +1,74 @@
+select('*');
+
+ $dataProvider = new ActiveDataProvider([
+ 'query' => $query,
+ ]);
+
+ $this->load($params);
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ $query->joinWith(['terminLangs', 'terminStructures']);
+
+ $query->andFilterWhere([
+ 'termin_id' => $this->termin_id,
+ 'is_book' => $this->is_book,
+ ]);
+
+ $query->andFilterWhere(['like', 'termin_name', $this->termin_name]);
+ $query->andFilterWhere(['like', TerminLang::tableName().'.termin_title', $this->termin_title]);
+ //$query->andFilterWhere(['like', 'termin_title', $this->termin_title]);
+
+ return $dataProvider;
+ }
+}
diff --git a/backend/models/TerminStructure.php b/backend/models/TerminStructure.php
new file mode 100644
index 0000000..2612eda
--- /dev/null
+++ b/backend/models/TerminStructure.php
@@ -0,0 +1,67 @@
+ Yii::t('app', 'Termin ID'),
+ 'termin_pid' => Yii::t('app', 'Termin Pid'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTermin()
+ {
+ return $this->hasOne(Termin::className(), ['termin_id' => 'termin_id']);
+ }
+
+ public function getParent()
+ {
+ return $this->hasOne(Termin::className(), ['termin_id' => 'termin_pid']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTerminPid()
+ {
+ return $this->hasMany(TerminLang::className(), ['termin_id' => 'termin_pid']);
+ }
+}
diff --git a/backend/models/UserSearch.php b/backend/models/UserSearch.php
new file mode 100644
index 0000000..bb01483
--- /dev/null
+++ b/backend/models/UserSearch.php
@@ -0,0 +1,76 @@
+ $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;
+ }
+
+ $query->andFilterWhere([
+ 'id' => $this->id,
+ 'status' => $this->status,
+ 'created_at' => $this->created_at,
+ 'updated_at' => $this->updated_at,
+ ]);
+
+ $query->andFilterWhere(['like', 'username', $this->username])
+ ->andFilterWhere(['like', 'lastname', $this->lastname])
+ ->andFilterWhere(['like', 'firstname', $this->firstname])
+ ->andFilterWhere(['like', 'middlename', $this->middlename])
+ ->andFilterWhere(['like', 'auth_key', $this->auth_key])
+ ->andFilterWhere(['like', 'password_hash', $this->password_hash])
+ ->andFilterWhere(['like', 'password_reset_token', $this->password_reset_token])
+ ->andFilterWhere(['like', 'email', $this->email]);
+
+ return $dataProvider;
+ }
+}
diff --git a/backend/runtime/.gitignore b/backend/runtime/.gitignore
new file mode 100644
index 0000000..c96a04f
--- /dev/null
+++ b/backend/runtime/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/backend/views/_language/_form.php b/backend/views/_language/_form.php
new file mode 100644
index 0000000..3119243
--- /dev/null
+++ b/backend/views/_language/_form.php
@@ -0,0 +1,27 @@
+
+
+
diff --git a/backend/views/_language/_search.php b/backend/views/_language/_search.php
new file mode 100644
index 0000000..b6c760e
--- /dev/null
+++ b/backend/views/_language/_search.php
@@ -0,0 +1,31 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'language_id') ?>
+
+ = $form->field($model, 'lang_code') ?>
+
+ = $form->field($model, 'is_default') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/backend/views/_language/create.php b/backend/views/_language/create.php
new file mode 100644
index 0000000..ca8185b
--- /dev/null
+++ b/backend/views/_language/create.php
@@ -0,0 +1,19 @@
+title = Yii::t('app', 'Create Language');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Languages'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;*/
+echo $this->render('index', $layoutdata);
+?>
+
+
+ = $this->render('_form', [
+ 'model' => $model
+ ]) ?>
+
\ No newline at end of file
diff --git a/backend/views/_language/index.php b/backend/views/_language/index.php
new file mode 100644
index 0000000..43938c7
--- /dev/null
+++ b/backend/views/_language/index.php
@@ -0,0 +1,86 @@
+registerCssFile('/backend/web/css/language.css');
+$this->registerJsFile('/backend/web/js/language.js', ['depends' => ['yii\web\JqueryAsset'], 'position' => $this::POS_END]);
+$this->title = Yii::t('app', 'Settings');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
= Html::encode($this->title) ?>
+
+
diff --git a/backend/views/_language/update.php b/backend/views/_language/update.php
new file mode 100644
index 0000000..4529adb
--- /dev/null
+++ b/backend/views/_language/update.php
@@ -0,0 +1,22 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Language',
+]) . ' ' . $model->language_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Languages'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->language_id, 'url' => ['view', 'id' => $model->language_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');*/
+echo $this->render('index', $layoutdata);
+?>
+
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/backend/views/_language/view.php b/backend/views/_language/view.php
new file mode 100644
index 0000000..56d4ae7
--- /dev/null
+++ b/backend/views/_language/view.php
@@ -0,0 +1,33 @@
+title = $model->language_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Languages'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;*/
+echo $this->render('index', $layoutdata);
+?>
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'language_id',
+ 'lang_code',
+ 'is_default',
+ ],
+ ]) ?>
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->language_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->language_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
\ No newline at end of file
diff --git a/backend/views/admin-menu/_form.php b/backend/views/admin-menu/_form.php
new file mode 100644
index 0000000..21c33c0
--- /dev/null
+++ b/backend/views/admin-menu/_form.php
@@ -0,0 +1,61 @@
+
+
+
diff --git a/backend/views/admin-menu/_search.php b/backend/views/admin-menu/_search.php
new file mode 100644
index 0000000..666ad9b
--- /dev/null
+++ b/backend/views/admin-menu/_search.php
@@ -0,0 +1,41 @@
+
+
+
diff --git a/backend/views/admin-menu/create.php b/backend/views/admin-menu/create.php
new file mode 100644
index 0000000..fe076d8
--- /dev/null
+++ b/backend/views/admin-menu/create.php
@@ -0,0 +1,22 @@
+title = Yii::t('app', 'Create Admin Menu');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Admin Menus'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/admin-menu/index.php b/backend/views/admin-menu/index.php
new file mode 100644
index 0000000..e75ad2a
--- /dev/null
+++ b/backend/views/admin-menu/index.php
@@ -0,0 +1,92 @@
+title = Yii::t('app', 'Admin Menus');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/admin-menu/update.php b/backend/views/admin-menu/update.php
new file mode 100644
index 0000000..b2aa38c
--- /dev/null
+++ b/backend/views/admin-menu/update.php
@@ -0,0 +1,19 @@
+title = Yii::t('app', 'Update') . ': ' . $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Admin Menus'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->admin_menu_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
diff --git a/backend/views/admin-menu/view.php b/backend/views/admin-menu/view.php
new file mode 100644
index 0000000..859ca41
--- /dev/null
+++ b/backend/views/admin-menu/view.php
@@ -0,0 +1,43 @@
+title = Yii::t('app', 'Admin Menus').': '.$model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Admin Menus'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
\ No newline at end of file
diff --git a/backend/views/blog/articles.php b/backend/views/blog/articles.php
new file mode 100644
index 0000000..b4d1e07
--- /dev/null
+++ b/backend/views/blog/articles.php
@@ -0,0 +1,32 @@
+ $dataProvider,
+ 'columns' => [
+ 'id',
+ 'code',
+ 'create_at',
+ [
+ 'value' => function($data) {
+ return $data->author0->firstname.' '.$data->author0->lastname;
+ },
+ 'header' => Yii::t('app', 'Author')
+ ],
+ [
+ 'class' => Column::className(),
+ 'header' => Yii::t('app', 'Name'),
+ 'content' => function($model, $key, $index, $column) {
+ return $model->getArticleLangs()->where(['language_id' => Language::getDefaultLang()->language_id])->one()->name;
+ }
+ ],
+ [
+ 'class' => ActionColumn::className(),
+ 'template' => '{update} {delete}'
+ ]
+ ]
+]);
\ No newline at end of file
diff --git a/backend/views/import/index.php b/backend/views/import/index.php
new file mode 100644
index 0000000..8862200
--- /dev/null
+++ b/backend/views/import/index.php
@@ -0,0 +1,33 @@
+
+
+
diff --git a/backend/views/language/_form_adress.php b/backend/views/language/_form_adress.php
new file mode 100644
index 0000000..36542a7
--- /dev/null
+++ b/backend/views/language/_form_adress.php
@@ -0,0 +1,119 @@
+
+
+
diff --git a/backend/views/language/_form_adress_edit.php b/backend/views/language/_form_adress_edit.php
new file mode 100644
index 0000000..6855c28
--- /dev/null
+++ b/backend/views/language/_form_adress_edit.php
@@ -0,0 +1,41 @@
+
+
+
diff --git a/backend/views/language/_search.php b/backend/views/language/_search.php
new file mode 100644
index 0000000..bd18a09
--- /dev/null
+++ b/backend/views/language/_search.php
@@ -0,0 +1,35 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'language_id') ?>
+
+ = $form->field($model, 'lang_code') ?>
+
+ = $form->field($model, 'is_default')->checkbox() ?>
+
+ = $form->field($model, 'language_name') ?>
+
+ = $form->field($model, 'active')->checkbox() ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/backend/views/language/create.php b/backend/views/language/create.php
new file mode 100644
index 0000000..4796204
--- /dev/null
+++ b/backend/views/language/create.php
@@ -0,0 +1,48 @@
+title = Yii::t('app', 'Languages');
+$this->params['breadcrumbs'][] = $this->title;
+echo $this->render('layout');
+?>
+
+
+ render('_search', ['model' => $searchModel]); ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'layout' => "{items}",
+ 'columns' => [
+ [
+ 'class' => Column::className(),
+ 'content' => function($model, $key, $index, $column) {
+ return '
';
+ }
+ ],
+ 'language_name',
+ 'lang_code',
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{create}',
+ 'buttons' => [
+ 'create' => function ($url, $model, $key) {
+ return Html::a('', $url, ['class' => 'glyphicon glyphicon-plus', 'title' => Yii::t('app', 'Create Language')]);
+ },
+ ],
+ ]
+ ]
+ ]); ?>
+
+
+ = Html::a(Yii::t('app', 'Cancel'), ['index'], ['class' => 'btn btn-success']) ?>
+
+
+
diff --git a/backend/views/language/create_adress.php b/backend/views/language/create_adress.php
new file mode 100644
index 0000000..04ff331
--- /dev/null
+++ b/backend/views/language/create_adress.php
@@ -0,0 +1,19 @@
+title = Yii::t('app', 'Create Option');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+echo $this->render('layout');
+?>
+
+ render('_form_adress', $oneform);
+ }?>
+
+
diff --git a/backend/views/language/index.php b/backend/views/language/index.php
new file mode 100644
index 0000000..ebd2c98
--- /dev/null
+++ b/backend/views/language/index.php
@@ -0,0 +1,49 @@
+title = Yii::t('app', 'Languages');
+$this->params['breadcrumbs'][] = $this->title;
+echo $this->render('layout');
+?>
+
+
+ render('_search', ['model' => $searchModel]); ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'layout' => "{items}",
+ 'columns' => [
+ [
+ 'class' => Column::className(),
+ 'content' => function($model, $key, $index, $column) {
+ return '
';
+ }
+ ],
+ 'language_name',
+ 'language_code',
+ 'is_default:boolean',
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{default} {delete}',
+ 'buttons' => [
+ 'default' => function ($url, $model, $key) {
+ return Html::a('', $model->is_default?'#':$url, ['class' => $model->is_default?'glyphicon glyphicon-star':'glyphicon glyphicon-star-empty', 'title' => Yii::t('app', 'Make default')]);
+ },
+ ],
+ ]
+ ]
+ ]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create Language'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+
diff --git a/backend/views/language/layout.php b/backend/views/language/layout.php
new file mode 100644
index 0000000..5103c47
--- /dev/null
+++ b/backend/views/language/layout.php
@@ -0,0 +1,43 @@
+registerCssFile('/backend/web/css/language.css');
+$this->registerJsFile('/backend/web/js/language.js', ['depends' => ['yii\web\JqueryAsset'], 'position' => $this::POS_END]);
+$this->title = Yii::t('app', 'Settings');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
= Html::encode($this->title) ?>
+
+
diff --git a/backend/views/language/update.php b/backend/views/language/update.php
new file mode 100644
index 0000000..fbfe669
--- /dev/null
+++ b/backend/views/language/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Language',
+]) . ' ' . $model->language_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Languages'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->language_id, 'url' => ['view', 'id' => $model->language_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/backend/views/language/update_adress.php b/backend/views/language/update_adress.php
new file mode 100644
index 0000000..26bd4fd
--- /dev/null
+++ b/backend/views/language/update_adress.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Option',
+]) . ' ' . $forms[0]['models'][\Yii::$app->request->get('id')]->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $forms[0]['models'][\Yii::$app->request->get('id')]->name, 'url' => ['view', 'id' => $forms[0]['models'][\Yii::$app->request->get('id')]->option_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+echo $this->render('layout');
+?>
+
+ render('_form_adress_edit', $oneform);
+ }?>
+
+
diff --git a/backend/views/language/view.php b/backend/views/language/view.php
new file mode 100644
index 0000000..356f305
--- /dev/null
+++ b/backend/views/language/view.php
@@ -0,0 +1,39 @@
+title = $model->language_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Languages'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->language_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->language_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'language_id',
+ 'lang_code',
+ 'is_default:boolean',
+ 'language_name',
+ 'active:boolean',
+ ],
+ ]) ?>
+
+
diff --git a/backend/views/language/view_adress.php b/backend/views/language/view_adress.php
new file mode 100644
index 0000000..3a91122
--- /dev/null
+++ b/backend/views/language/view_adress.php
@@ -0,0 +1,50 @@
+title = Yii::t('app', 'Languages');
+$this->params['breadcrumbs'][] = $this->title;
+echo $this->render('layout');
+?>
+
+
+ render('_search', ['model' => $searchModel]); ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'layout' => "{items}",
+ 'columns' => [
+ [
+ 'class' => 'yii\grid\Column',
+ 'content' => function($model, $key, $index, $column) {
+ return OptionLang::find()->select('value')->where(['language_id' => 0, 'id' => $model->option_id])->scalar();
+ },
+ 'header' => Yii::t('app', 'adress_name')
+ ],
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{update-adress} {delete-adress}',
+ 'buttons' => [
+ 'update-adress' => function ($url, $model, $key) {
+ return Html::a('', $url, ['class' => 'glyphicon glyphicon-pencil', 'title' => Yii::t('app', 'Change')]);
+ },
+ 'delete-adress' => function ($url, $model, $key) {
+ return Html::a('', $url, ['class' => 'glyphicon glyphicon-trash', 'title' => Yii::t('app', 'Delete')]);
+ },
+ ],
+ ]
+ ]
+ ]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create adress'), ['create-adress'], ['class' => 'btn btn-success']) ?>
+
+
+
diff --git a/backend/views/layouts/content.php b/backend/views/layouts/content.php
new file mode 100644
index 0000000..d19d9b0
--- /dev/null
+++ b/backend/views/layouts/content.php
@@ -0,0 +1,241 @@
+
+
+
+
+
+ = Alert::widget() ?>
+ = $content ?>
+
+
+
+
+
+ Version 2.0
+
+ Copyright © 2014-2015 Almsaeed Studio . All rights
+ reserved.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/views/layouts/header.php b/backend/views/layouts/header.php
new file mode 100644
index 0000000..8b3e9db
--- /dev/null
+++ b/backend/views/layouts/header.php
@@ -0,0 +1,300 @@
+user->identity->firstname.' '.Yii::$app->user->identity->lastname;
+
+/* @var $this \yii\web\View */
+/* @var $content string */
+?>
+
+
+
+ = Html::a('APP ' . Yii::$app->name . ' ', Yii::$app->homeUrl, ['class' => 'logo']) ?>
+
+
+
+
+
+
+
+
diff --git a/backend/views/layouts/left.php b/backend/views/layouts/left.php
new file mode 100644
index 0000000..0660ccd
--- /dev/null
+++ b/backend/views/layouts/left.php
@@ -0,0 +1,29 @@
+
diff --git a/backend/views/layouts/main-login.php b/backend/views/layouts/main-login.php
new file mode 100644
index 0000000..c6525db
--- /dev/null
+++ b/backend/views/layouts/main-login.php
@@ -0,0 +1,29 @@
+
+beginPage() ?>
+
+
+
+
+
+ = Html::csrfMetaTags() ?>
+ = Html::encode($this->title) ?>
+ head() ?>
+
+
+
+beginBody() ?>
+
+ = $content ?>
+
+endBody() ?>
+
+
+endPage() ?>
diff --git a/backend/views/layouts/main.php b/backend/views/layouts/main.php
new file mode 100644
index 0000000..1aa871f
--- /dev/null
+++ b/backend/views/layouts/main.php
@@ -0,0 +1,65 @@
+controller->action->id === 'login') {
+/**
+ * Do not use this code in your template. Remove it.
+ * Instead, use the code $this->layout = '//main-login'; in your controller.
+ */
+ echo $this->render(
+ 'main-login',
+ ['content' => $content]
+ );
+} else {
+
+ if (class_exists('backend\assets\AppAsset')) {
+ backend\assets\AppAsset::register($this);
+ } else {
+ app\assets\AppAsset::register($this);
+ }
+
+ dmstr\web\AdminLteAsset::register($this);
+
+ $directoryAsset = Yii::$app->assetManager->getPublishedUrl('@vendor/almasaeed2010/adminlte/dist');
+ ?>
+ beginPage() ?>
+
+
+
+
+
+ = Html::csrfMetaTags() ?>
+ = Html::encode($this->title) ?>
+ head() ?>
+
+
+ beginBody() ?>
+
+
+ = $this->render(
+ 'header.php',
+ ['directoryAsset' => $directoryAsset]
+ ) ?>
+
+ = $this->render(
+ 'left.php',
+ ['directoryAsset' => $directoryAsset]
+ )
+ ?>
+
+ = $this->render(
+ 'content.php',
+ ['content' => $content, 'directoryAsset' => $directoryAsset]
+ ) ?>
+
+
+
+ endBody() ?>
+
+
+ endPage() ?>
+
diff --git a/backend/views/layouts/settings.php b/backend/views/layouts/settings.php
new file mode 100644
index 0000000..6862d3a
--- /dev/null
+++ b/backend/views/layouts/settings.php
@@ -0,0 +1,40 @@
+beginContent('@app/views/layouts/main.php');
+?>
+
+
+
+
+
+ [
+ 'class' => 'nav nav-pills nav-stacked'
+ ],
+ 'items' => [
+ ['label' => Yii::t('app', 'Admin Menus'), 'url' => ['/admin-menu/index'], 'icon' => 'fa fa-navicon'],
+ ['label' => Yii::t('app', 'Next menu item'), 'url' => ['#'], 'icon' => 'fa fa-arrow-circle-o-right'],
+ ],
+ ]);
+ ?>
+
+
+
+
+ = $content ?>
+
+
+endContent() ?>
\ No newline at end of file
diff --git a/backend/views/menu-location/_form.php b/backend/views/menu-location/_form.php
new file mode 100644
index 0000000..2d51e5c
--- /dev/null
+++ b/backend/views/menu-location/_form.php
@@ -0,0 +1,27 @@
+
+
+
diff --git a/backend/views/menu-location/_search.php b/backend/views/menu-location/_search.php
new file mode 100644
index 0000000..898e4db
--- /dev/null
+++ b/backend/views/menu-location/_search.php
@@ -0,0 +1,29 @@
+
+
+
diff --git a/backend/views/menu-location/create.php b/backend/views/menu-location/create.php
new file mode 100644
index 0000000..05abb25
--- /dev/null
+++ b/backend/views/menu-location/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Create Menu Location');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Menu Locations'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/menu-location/index.php b/backend/views/menu-location/index.php
new file mode 100644
index 0000000..570e058
--- /dev/null
+++ b/backend/views/menu-location/index.php
@@ -0,0 +1,43 @@
+title = Yii::t('app', 'Menu Locations');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/menu-location/update.php b/backend/views/menu-location/update.php
new file mode 100644
index 0000000..2b34243
--- /dev/null
+++ b/backend/views/menu-location/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Menu Location',
+]) . ' ' . $model->menu_location_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Menu Locations'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->menu_location_id, 'url' => ['view', 'id' => $model->menu_location_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
diff --git a/backend/views/menu-location/view.php b/backend/views/menu-location/view.php
new file mode 100644
index 0000000..507515c
--- /dev/null
+++ b/backend/views/menu-location/view.php
@@ -0,0 +1,36 @@
+title = $model->menu_location_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Menu Locations'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/menu/_form.php b/backend/views/menu/_form.php
new file mode 100644
index 0000000..43b9686
--- /dev/null
+++ b/backend/views/menu/_form.php
@@ -0,0 +1,41 @@
+
+
+
diff --git a/backend/views/menu/_search.php b/backend/views/menu/_search.php
new file mode 100644
index 0000000..7dd531e
--- /dev/null
+++ b/backend/views/menu/_search.php
@@ -0,0 +1,45 @@
+
+
+
diff --git a/backend/views/menu/create.php b/backend/views/menu/create.php
new file mode 100644
index 0000000..1b8eb6e
--- /dev/null
+++ b/backend/views/menu/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Create Menu');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Menus'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/menu/index.php b/backend/views/menu/index.php
new file mode 100644
index 0000000..2be0f94
--- /dev/null
+++ b/backend/views/menu/index.php
@@ -0,0 +1,44 @@
+title = Yii::t('app', 'Menus');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/menu/update.php b/backend/views/menu/update.php
new file mode 100644
index 0000000..cb96a01
--- /dev/null
+++ b/backend/views/menu/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Menu',
+]) . ' ' . $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Menus'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->menu_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
diff --git a/backend/views/menu/view.php b/backend/views/menu/view.php
new file mode 100644
index 0000000..4033dcb
--- /dev/null
+++ b/backend/views/menu/view.php
@@ -0,0 +1,44 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Menus'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/new-options-lang/_form.php b/backend/views/new-options-lang/_form.php
new file mode 100644
index 0000000..26fff33
--- /dev/null
+++ b/backend/views/new-options-lang/_form.php
@@ -0,0 +1,27 @@
+
+
+
diff --git a/backend/views/new-options-lang/_search.php b/backend/views/new-options-lang/_search.php
new file mode 100644
index 0000000..c146c4f
--- /dev/null
+++ b/backend/views/new-options-lang/_search.php
@@ -0,0 +1,33 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'primary') ?>
+
+ = $form->field($model, 'id') ?>
+
+ = $form->field($model, 'language_id') ?>
+
+ = $form->field($model, 'value') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/backend/views/new-options-lang/create.php b/backend/views/new-options-lang/create.php
new file mode 100644
index 0000000..3f3c43b
--- /dev/null
+++ b/backend/views/new-options-lang/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Create New Options Lang');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'New Options Langs'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/backend/views/new-options-lang/index.php b/backend/views/new-options-lang/index.php
new file mode 100644
index 0000000..907b488
--- /dev/null
+++ b/backend/views/new-options-lang/index.php
@@ -0,0 +1,37 @@
+title = Yii::t('app', 'New Options Langs');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create New Options Lang'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'primary',
+ 'id',
+ 'language_id',
+ 'value:ntext',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+
diff --git a/backend/views/new-options-lang/update.php b/backend/views/new-options-lang/update.php
new file mode 100644
index 0000000..b1889b6
--- /dev/null
+++ b/backend/views/new-options-lang/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'New Options Lang',
+]) . ' ' . $model->primary;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'New Options Langs'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->primary, 'url' => ['view', 'id' => $model->primary]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/backend/views/new-options-lang/view.php b/backend/views/new-options-lang/view.php
new file mode 100644
index 0000000..29bb711
--- /dev/null
+++ b/backend/views/new-options-lang/view.php
@@ -0,0 +1,38 @@
+title = $model->primary;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'New Options Langs'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->primary], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->primary], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'primary',
+ 'id',
+ 'language_id',
+ 'value:ntext',
+ ],
+ ]) ?>
+
+
diff --git a/backend/views/option/_form.php b/backend/views/option/_form.php
new file mode 100644
index 0000000..32331a3
--- /dev/null
+++ b/backend/views/option/_form.php
@@ -0,0 +1,128 @@
+
+
+
diff --git a/backend/views/option/_form_edit.php b/backend/views/option/_form_edit.php
new file mode 100644
index 0000000..f5384ce
--- /dev/null
+++ b/backend/views/option/_form_edit.php
@@ -0,0 +1,41 @@
+
+
+
diff --git a/backend/views/option/_search.php b/backend/views/option/_search.php
new file mode 100644
index 0000000..5fe0200
--- /dev/null
+++ b/backend/views/option/_search.php
@@ -0,0 +1,37 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'option_id') ?>
+
+ = $form->field($model, 'model') ?>
+
+ = $form->field($model, 'model_id') ?>
+
+ = $form->field($model, 'name') ?>
+
+ = $form->field($model, 'template') ?>
+
+ field($model, 'parent_id') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/backend/views/option/create.php b/backend/views/option/create.php
new file mode 100644
index 0000000..7c128cc
--- /dev/null
+++ b/backend/views/option/create.php
@@ -0,0 +1,19 @@
+title = Yii::t('app', 'Create Option');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+ render('_form', $oneform);
+ }?>
+
+
diff --git a/backend/views/option/index.php b/backend/views/option/index.php
new file mode 100644
index 0000000..9deb2f6
--- /dev/null
+++ b/backend/views/option/index.php
@@ -0,0 +1,71 @@
+title = Yii::t('app', 'Options');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create Option'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'option_id',
+ 'model',
+ 'model_id',
+ 'name',
+ 'template',
+ // 'parent_id',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+
+
+
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create Option'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'option_id',
+ 'model',
+ 'model_id',
+ 'name',
+ 'template',
+ // 'parent_id',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+
+
+
\ No newline at end of file
diff --git a/backend/views/option/update.php b/backend/views/option/update.php
new file mode 100644
index 0000000..1bb652d
--- /dev/null
+++ b/backend/views/option/update.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Option',
+]) . ' ' . $forms[0]['models'][\Yii::$app->request->get('id')]->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $forms[0]['models'][\Yii::$app->request->get('id')]->name, 'url' => ['view', 'id' => $forms[0]['models'][\Yii::$app->request->get('id')]->option_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
= Html::encode($this->title) ?>
+ render('_form_edit', $oneform);
+ }?>
+
+
diff --git a/backend/views/option/view.php b/backend/views/option/view.php
new file mode 100644
index 0000000..ba4d20b
--- /dev/null
+++ b/backend/views/option/view.php
@@ -0,0 +1,40 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->option_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->option_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'option_id',
+ 'model',
+ 'model_id',
+ 'name',
+ 'template',
+ 'parent_id',
+ ],
+ ]) ?>
+
+
diff --git a/backend/views/settings/index.php b/backend/views/settings/index.php
new file mode 100644
index 0000000..8850caa
--- /dev/null
+++ b/backend/views/settings/index.php
@@ -0,0 +1,11 @@
+title = Yii::t('app', 'Settings');
+$this->params['breadcrumbs'][] = $this->title;
+?>
diff --git a/backend/views/site/error.php b/backend/views/site/error.php
new file mode 100644
index 0000000..0ba2574
--- /dev/null
+++ b/backend/views/site/error.php
@@ -0,0 +1,27 @@
+title = $name;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = nl2br(Html::encode($message)) ?>
+
+
+
+ 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 100644
index 0000000..50858ce
--- /dev/null
+++ b/backend/views/site/index.php
@@ -0,0 +1,55 @@
+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 100644
index 0000000..9903776
--- /dev/null
+++ b/backend/views/site/login.php
@@ -0,0 +1,37 @@
+title = 'Login';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+
+
Please fill out the following fields to login:
+
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias aperiam aspernatur esse hic inventore iusto labore perspiciatis! Aliquam eligendi illum incidunt ipsum laboriosam nam, nostrum, odit possimus saepe unde voluptate!
+
+ 'login-form']); ?>
+
+ = $form->field($model, 'username') ?>
+
+ = $form->field($model, 'password')->passwordInput() ?>
+
+ = $form->field($model, 'rememberMe')->checkbox() ?>
+
+
+ = Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
+
+
+
+
+
+
+
diff --git a/backend/views/site/profile.php b/backend/views/site/profile.php
new file mode 100644
index 0000000..d230621
--- /dev/null
+++ b/backend/views/site/profile.php
@@ -0,0 +1,41 @@
+title = 'Profile';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/backend/views/site/requests.php b/backend/views/site/requests.php
new file mode 100644
index 0000000..3203326
--- /dev/null
+++ b/backend/views/site/requests.php
@@ -0,0 +1,70 @@
+title = Yii::t('app', 'Requests');
+?>
+
+ $dataProvider,
+ 'columns' => [
+ [
+ 'class' => 'yii\grid\SerialColumn'
+ ],
+ [
+ 'attribute' => 'created_at',
+ ],
+ [
+ 'header' => Yii::t('app', 'values'),
+ 'content' => function($model, $key, $index, $column) {
+ $value = '';
+ $langs = $model->options;
+ $lang = $model->getOptionDefaultLang(true);
+ $value .= "{$model->name} :{$lang['value']}";
+ foreach($langs as $onemodel) {
+ $lang = $onemodel->getOptionDefaultLang(true);
+ $value .= "{$onemodel->name} :{$lang['value']}";
+ }
+ return $value;
+ }
+ ],
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{requests} {delete-req}',
+ 'buttons' => [
+ 'requests' => function($url, $model, $key) {
+ return Html::a(
+ '',
+ $model->options['is_new']->getOptionDefaultLang()->value?$url:'#',
+ [
+ 'class' => $model->options['is_new']->getOptionDefaultLang()->value?'glyphicon glyphicon-eye-open':'glyphicon glyphicon-eye-close',
+ 'title' => Yii::t('app', 'Make already read')
+ ]
+ );
+ },
+ 'delete-req' => function($url, $model, $key) {
+ return Html::a(
+ '',
+ $url,
+ [
+ 'class' => 'glyphicon glyphicon-trash',
+ 'title' => Yii::t('app', 'Delete'),
+ 'data' => [
+ 'label' => Yii::t('app', 'Delete'),
+ 'confirm' => Yii::t('app', 'Are you sure you want delete this element?'),
+ 'method' => 'post',
+ 'pjax' => 0
+ ]
+ ]
+ );
+ }
+ ]
+ ]
+ ],
+ ]);
+ ?>
+
diff --git a/backend/views/termin/_article_form.php b/backend/views/termin/_article_form.php
new file mode 100644
index 0000000..19d0dca
--- /dev/null
+++ b/backend/views/termin/_article_form.php
@@ -0,0 +1,19 @@
+
+
+
+ = (new ActiveField(['model' => $article_lang, 'attribute' => "[$model->language_id]lang_id"]))->label(false)->hiddenInput(['value' => $model->language_id]) ?>
+
+ = (new ActiveField(['model' => $article_lang, 'attribute' => "[$model->language_id]termin_title"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $article_lang, 'attribute' => "[$model->language_id]termin_alias"]))->textInput() ?>
+
+
+end();
+?>
diff --git a/backend/views/termin/_form.php b/backend/views/termin/_form.php
new file mode 100644
index 0000000..1863c91
--- /dev/null
+++ b/backend/views/termin/_form.php
@@ -0,0 +1,50 @@
+
+
+
diff --git a/backend/views/termin/_search.php b/backend/views/termin/_search.php
new file mode 100644
index 0000000..1ff1d09
--- /dev/null
+++ b/backend/views/termin/_search.php
@@ -0,0 +1,31 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'termin_id') ?>
+
+ = $form->field($model, 'termin_name') ?>
+
+ = $form->field($model, 'is_book') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/backend/views/termin/create.php b/backend/views/termin/create.php
new file mode 100644
index 0000000..2be42d1
--- /dev/null
+++ b/backend/views/termin/create.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Create').' '.Yii::t('app', 'termin');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Termins'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'model_lang' => $model_lang,
+ 'model_pid' => $model_pid,
+ ]) ?>
+
+
diff --git a/backend/views/termin/index.php b/backend/views/termin/index.php
new file mode 100644
index 0000000..c65c4ef
--- /dev/null
+++ b/backend/views/termin/index.php
@@ -0,0 +1,45 @@
+title = Yii::t('app', 'termin');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ [
+ 'attribute' => 'termin_title',
+ //'value' => 'terminLangs.termin_title'
+ ],
+ [
+ 'attribute' => 'termin_parent_title',
+ 'value' => 'terminStructures.parent.terminLangs.termin_title',
+ 'content' => function($model, $key, $index, $column) {
+ return $model->terminStructures[0]->parent->terminLangs[0]->termin_title;
+ }
+ ],
+ 'termin_name',
+ 'is_book',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+
diff --git a/backend/views/termin/update.php b/backend/views/termin/update.php
new file mode 100644
index 0000000..4b2e1e4
--- /dev/null
+++ b/backend/views/termin/update.php
@@ -0,0 +1,26 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Termin',
+]) . ' ' . $model->termin_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Termins'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->termin_id, 'url' => ['view', 'id' => $model->termin_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'model_lang' => $model_lang,
+ 'model_pid' => $model_pid,
+ ]) ?>
+
+
+
diff --git a/backend/views/termin/view.php b/backend/views/termin/view.php
new file mode 100644
index 0000000..acc4659
--- /dev/null
+++ b/backend/views/termin/view.php
@@ -0,0 +1,37 @@
+title = $model->termin_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Termins'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->termin_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->termin_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'termin_id',
+ 'termin_name',
+ 'is_book',
+ ],
+ ]) ?>
+
+
diff --git a/backend/views/user/_form.php b/backend/views/user/_form.php
new file mode 100644
index 0000000..cbdf5dc
--- /dev/null
+++ b/backend/views/user/_form.php
@@ -0,0 +1,23 @@
+
+
+
diff --git a/backend/views/user/_search.php b/backend/views/user/_search.php
new file mode 100644
index 0000000..5fc3f1a
--- /dev/null
+++ b/backend/views/user/_search.php
@@ -0,0 +1,49 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'id') ?>
+
+ = $form->field($model, 'username') ?>
+
+ = $form->field($model, 'lastname') ?>
+
+ = $form->field($model, 'firstname') ?>
+
+ = $form->field($model, 'middlename') ?>
+
+ field($model, 'auth_key') ?>
+
+ field($model, 'password_hash') ?>
+
+ field($model, 'password_reset_token') ?>
+
+ field($model, 'email') ?>
+
+ field($model, 'status') ?>
+
+ field($model, 'created_at') ?>
+
+ field($model, 'updated_at') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/backend/views/user/create.php b/backend/views/user/create.php
new file mode 100644
index 0000000..52adf3c
--- /dev/null
+++ b/backend/views/user/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Create User');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Users'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/backend/views/user/index.php b/backend/views/user/index.php
new file mode 100644
index 0000000..8e3284d
--- /dev/null
+++ b/backend/views/user/index.php
@@ -0,0 +1,55 @@
+title = Yii::t('app', 'Users');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create User'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'id',
+ 'username',
+ 'lastname',
+ 'firstname',
+ 'middlename',
+ // 'auth_key',
+ // 'password_hash',
+ // 'password_reset_token',
+ // 'email:email',
+ // 'status',
+ // 'created_at',
+ // 'updated_at',
+
+ ['class' => 'yii\grid\ActionColumn',
+ 'template' => '{view} {update} {permit} {delete}',
+ 'buttons' =>
+ [
+ 'permit' => function ($url, $model) {
+ return Html::a('
', Url::to(['/permit/user/view', 'id' => $model->id]), [
+ 'title' => Yii::t('yii', 'Change user role')
+ ]); },
+ ]
+ ],
+ ],
+ ]); ?>
+
+
diff --git a/backend/views/user/update.php b/backend/views/user/update.php
new file mode 100644
index 0000000..2e03759
--- /dev/null
+++ b/backend/views/user/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'User',
+]) . ' ' . $model->id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Users'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/backend/views/user/view.php b/backend/views/user/view.php
new file mode 100644
index 0000000..5709137
--- /dev/null
+++ b/backend/views/user/view.php
@@ -0,0 +1,46 @@
+title = $model->id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Users'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'id',
+ 'username',
+ 'lastname',
+ 'firstname',
+ 'middlename',
+ 'auth_key',
+ 'password_hash',
+ 'password_reset_token',
+ 'email:email',
+ 'status',
+ 'created_at',
+ 'updated_at',
+ ],
+ ]) ?>
+
+
diff --git a/backend/web/.gitignore b/backend/web/.gitignore
new file mode 100644
index 0000000..25c74e6
--- /dev/null
+++ b/backend/web/.gitignore
@@ -0,0 +1,2 @@
+/index.php
+/index-test.php
diff --git a/backend/web/assets/.gitignore b/backend/web/assets/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/backend/web/assets/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/backend/web/css/flags32.css b/backend/web/css/flags32.css
new file mode 100644
index 0000000..ba24b4f
--- /dev/null
+++ b/backend/web/css/flags32.css
@@ -0,0 +1,253 @@
+.f32 .flag{display:inline-block;height:32px;width:32px;vertical-align:text-top;line-height:32px;background:url(../images/flags32.png) no-repeat;}
+.f32 ._African_Union{background-position:0 -32px;}
+.f32 ._Arab_League{background-position:0 -64px;}
+.f32 ._ASEAN{background-position:0 -96px;}
+.f32 ._CARICOM{background-position:0 -128px;}
+.f32 ._CIS{background-position:0 -160px;}
+.f32 ._Commonwealth{background-position:0 -192px;}
+.f32 ._England{background-position:0 -224px;}
+.f32 ._European_Union, .f32 .eu{background-position:0 -256px;}
+.f32 ._Islamic_Conference{background-position:0 -288px;}
+.f32 ._Kosovo{background-position:0 -320px;}
+.f32 ._NATO{background-position:0 -352px;}
+.f32 ._Northern_Cyprus{background-position:0 -384px;}
+.f32 ._Northern_Ireland{background-position:0 -416px;}
+.f32 ._Olimpic_Movement{background-position:0 -448px;}
+.f32 ._OPEC{background-position:0 -480px;}
+.f32 ._Red_Cross{background-position:0 -512px;}
+.f32 ._Scotland{background-position:0 -544px;}
+.f32 ._Somaliland{background-position:0 -576px;}
+.f32 ._Tibet{background-position:0 -608px;}
+.f32 ._United_Nations{background-position:0 -640px;}
+.f32 ._Wales{background-position:0 -672px;}
+.f32 .ad{background-position:0 -704px;}
+.f32 .ae{background-position:0 -736px;}
+.f32 .af{background-position:0 -768px;}
+.f32 .ag{background-position:0 -800px;}
+.f32 .ai{background-position:0 -832px;}
+.f32 .al{background-position:0 -864px;}
+.f32 .am{background-position:0 -896px;}
+.f32 .ao{background-position:0 -928px;}
+.f32 .aq{background-position:0 -960px;}
+.f32 .ar{background-position:0 -992px;}
+.f32 .as{background-position:0 -1024px;}
+.f32 .at{background-position:0 -1056px;}
+.f32 .au{background-position:0 -1088px;}
+.f32 .aw{background-position:0 -1120px;}
+.f32 .ax{background-position:0 -1152px;}
+.f32 .az{background-position:0 -1184px;}
+.f32 .ba{background-position:0 -1216px;}
+.f32 .bb{background-position:0 -1248px;}
+.f32 .bd{background-position:0 -1280px;}
+.f32 .be{background-position:0 -1312px;}
+.f32 .bf{background-position:0 -1344px;}
+.f32 .bg{background-position:0 -1376px;}
+.f32 .bh{background-position:0 -1408px;}
+.f32 .bi{background-position:0 -1440px;}
+.f32 .bj{background-position:0 -1472px;}
+.f32 .bm{background-position:0 -1504px;}
+.f32 .bn{background-position:0 -1536px;}
+.f32 .bo{background-position:0 -1568px;}
+.f32 .br{background-position:0 -1600px;}
+.f32 .bs{background-position:0 -1632px;}
+.f32 .bt{background-position:0 -1664px;}
+.f32 .bw{background-position:0 -1696px;}
+.f32 .by{background-position:0 -1728px;}
+.f32 .bz{background-position:0 -1760px;}
+.f32 .ca{background-position:0 -1792px;}
+.f32 .cd{background-position:0 -1824px;}
+.f32 .cf{background-position:0 -1856px;}
+.f32 .cg{background-position:0 -1888px;}
+.f32 .ch{background-position:0 -1920px;}
+.f32 .ci{background-position:0 -1952px;}
+.f32 .ck{background-position:0 -1984px;}
+.f32 .cl{background-position:0 -2016px;}
+.f32 .cm{background-position:0 -2048px;}
+.f32 .cn{background-position:0 -2080px;}
+.f32 .co{background-position:0 -2112px;}
+.f32 .cr{background-position:0 -2144px;}
+.f32 .cu{background-position:0 -2176px;}
+.f32 .cv{background-position:0 -2208px;}
+.f32 .cy{background-position:0 -2240px;}
+.f32 .cz{background-position:0 -2272px;}
+.f32 .de{background-position:0 -2304px;}
+.f32 .dj{background-position:0 -2336px;}
+.f32 .dk{background-position:0 -2368px;}
+.f32 .dm{background-position:0 -2400px;}
+.f32 .do{background-position:0 -2432px;}
+.f32 .dz{background-position:0 -2464px;}
+.f32 .ec{background-position:0 -2496px;}
+.f32 .ee{background-position:0 -2528px;}
+.f32 .eg{background-position:0 -2560px;}
+.f32 .eh{background-position:0 -2592px;}
+.f32 .er{background-position:0 -2624px;}
+.f32 .es{background-position:0 -2656px;}
+.f32 .et{background-position:0 -2688px;}
+.f32 .fi{background-position:0 -2720px;}
+.f32 .fj{background-position:0 -2752px;}
+.f32 .fm{background-position:0 -2784px;}
+.f32 .fo{background-position:0 -2816px;}
+.f32 .fr{background-position:0 -2848px;} .f32 .bl, .f32 .cp, .f32 .mf, .f32 .yt{background-position:0 -2848px;}
+.f32 .ga{background-position:0 -2880px;}
+.f32 .gb{background-position:0 -2912px;} .f32 .sh{background-position:0 -2912px;}
+.f32 .gd{background-position:0 -2944px;}
+.f32 .ge{background-position:0 -2976px;}
+.f32 .gg{background-position:0 -3008px;}
+.f32 .gh{background-position:0 -3040px;}
+.f32 .gi{background-position:0 -3072px;}
+.f32 .gl{background-position:0 -3104px;}
+.f32 .gm{background-position:0 -3136px;}
+.f32 .gn{background-position:0 -3168px;}
+.f32 .gp{background-position:0 -3200px;}
+.f32 .gq{background-position:0 -3232px;}
+.f32 .gr{background-position:0 -3264px;}
+.f32 .gt{background-position:0 -3296px;}
+.f32 .gu{background-position:0 -3328px;}
+.f32 .gw{background-position:0 -3360px;}
+.f32 .gy{background-position:0 -3392px;}
+.f32 .hk{background-position:0 -3424px;}
+.f32 .hn{background-position:0 -3456px;}
+.f32 .hr{background-position:0 -3488px;}
+.f32 .ht{background-position:0 -3520px;}
+.f32 .hu{background-position:0 -3552px;}
+.f32 .id{background-position:0 -3584px;}
+.f32 .mc{background-position:0 -3584px;}
+.f32 .ie{background-position:0 -3616px;}
+.f32 .il{background-position:0 -3648px;}
+.f32 .im{background-position:0 -3680px;}
+.f32 .in{background-position:0 -3712px;}
+.f32 .iq{background-position:0 -3744px;}
+.f32 .ir{background-position:0 -3776px;}
+.f32 .is{background-position:0 -3808px;}
+.f32 .it{background-position:0 -3840px;}
+.f32 .je{background-position:0 -3872px;}
+.f32 .jm{background-position:0 -3904px;}
+.f32 .jo{background-position:0 -3936px;}
+.f32 .jp{background-position:0 -3968px;}
+.f32 .ke{background-position:0 -4000px;}
+.f32 .kg{background-position:0 -4032px;}
+.f32 .kh{background-position:0 -4064px;}
+.f32 .ki{background-position:0 -4096px;}
+.f32 .km{background-position:0 -4128px;}
+.f32 .kn{background-position:0 -4160px;}
+.f32 .kp{background-position:0 -4192px;}
+.f32 .kr{background-position:0 -4224px;}
+.f32 .kw{background-position:0 -4256px;}
+.f32 .ky{background-position:0 -4288px;}
+.f32 .kz{background-position:0 -4320px;}
+.f32 .la{background-position:0 -4352px;}
+.f32 .lb{background-position:0 -4384px;}
+.f32 .lc{background-position:0 -4416px;}
+.f32 .li{background-position:0 -4448px;}
+.f32 .lk{background-position:0 -4480px;}
+.f32 .lr{background-position:0 -4512px;}
+.f32 .ls{background-position:0 -4544px;}
+.f32 .lt{background-position:0 -4576px;}
+.f32 .lu{background-position:0 -4608px;}
+.f32 .lv{background-position:0 -4640px;}
+.f32 .ly{background-position:0 -4672px;}
+.f32 .ma{background-position:0 -4704px;}
+.f32 .md{background-position:0 -4736px;}
+.f32 .me{background-position:0 -4768px;}
+.f32 .mg{background-position:0 -4800px;}
+.f32 .mh{background-position:0 -4832px;}
+.f32 .mk{background-position:0 -4864px;}
+.f32 .ml{background-position:0 -4896px;}
+.f32 .mm{background-position:0 -4928px;}
+.f32 .mn{background-position:0 -4960px;}
+.f32 .mo{background-position:0 -4992px;}
+.f32 .mq{background-position:0 -5024px;}
+.f32 .mr{background-position:0 -5056px;}
+.f32 .ms{background-position:0 -5088px;}
+.f32 .mt{background-position:0 -5120px;}
+.f32 .mu{background-position:0 -5152px;}
+.f32 .mv{background-position:0 -5184px;}
+.f32 .mw{background-position:0 -5216px;}
+.f32 .mx{background-position:0 -5248px;}
+.f32 .my{background-position:0 -5280px;}
+.f32 .mz{background-position:0 -5312px;}
+.f32 .na{background-position:0 -5344px;}
+.f32 .nc{background-position:0 -5376px;}
+.f32 .ne{background-position:0 -5408px;}
+.f32 .ng{background-position:0 -5440px;}
+.f32 .ni{background-position:0 -5472px;}
+.f32 .nl{background-position:0 -5504px;} .f32 .bq{background-position:0 -5504px;}
+.f32 .no{background-position:0 -5536px;} .f32 .bv, .f32 .nq, .f32 .sj{background-position:0 -5536px;}
+.f32 .np{background-position:0 -5568px;}
+.f32 .nr{background-position:0 -5600px;}
+.f32 .nz{background-position:0 -5632px;}
+.f32 .om{background-position:0 -5664px;}
+.f32 .pa{background-position:0 -5696px;}
+.f32 .pe{background-position:0 -5728px;}
+.f32 .pf{background-position:0 -5760px;}
+.f32 .pg{background-position:0 -5792px;}
+.f32 .ph{background-position:0 -5824px;}
+.f32 .pk{background-position:0 -5856px;}
+.f32 .pl{background-position:0 -5888px;}
+.f32 .pr{background-position:0 -5920px;}
+.f32 .ps{background-position:0 -5952px;}
+.f32 .pt{background-position:0 -5984px;}
+.f32 .pw{background-position:0 -6016px;}
+.f32 .py{background-position:0 -6048px;}
+.f32 .qa{background-position:0 -6080px;}
+.f32 .re{background-position:0 -6112px;}
+.f32 .ro{background-position:0 -6144px;}
+.f32 .rs{background-position:0 -6176px;}
+.f32 .ru{background-position:0 -6208px;}
+.f32 .rw{background-position:0 -6240px;}
+.f32 .sa{background-position:0 -6272px;}
+.f32 .sb{background-position:0 -6304px;}
+.f32 .sc{background-position:0 -6336px;}
+.f32 .sd{background-position:0 -6368px;}
+.f32 .se{background-position:0 -6400px;}
+.f32 .sg{background-position:0 -6432px;}
+.f32 .si{background-position:0 -6464px;}
+.f32 .sk{background-position:0 -6496px;}
+.f32 .sl{background-position:0 -6528px;}
+.f32 .sm{background-position:0 -6560px;}
+.f32 .sn{background-position:0 -6592px;}
+.f32 .so{background-position:0 -6624px;}
+.f32 .sr{background-position:0 -6656px;}
+.f32 .st{background-position:0 -6688px;}
+.f32 .sv{background-position:0 -6720px;}
+.f32 .sy{background-position:0 -6752px;}
+.f32 .sz{background-position:0 -6784px;}
+.f32 .tc{background-position:0 -6816px;}
+.f32 .td{background-position:0 -6848px;}
+.f32 .tg{background-position:0 -6880px;}
+.f32 .th{background-position:0 -6912px;}
+.f32 .tj{background-position:0 -6944px;}
+.f32 .tl{background-position:0 -6976px;}
+.f32 .tm{background-position:0 -7008px;}
+.f32 .tn{background-position:0 -7040px;}
+.f32 .to{background-position:0 -7072px;}
+.f32 .tr{background-position:0 -7104px;}
+.f32 .tt{background-position:0 -7136px;}
+.f32 .tv{background-position:0 -7168px;}
+.f32 .tw{background-position:0 -7200px;}
+.f32 .tz{background-position:0 -7232px;}
+.f32 .ua{background-position:0 -7264px;}
+.f32 .ug{background-position:0 -7296px;}
+.f32 .us{background-position:0 -7328px;}
+.f32 .uy{background-position:0 -7360px;}
+.f32 .uz{background-position:0 -7392px;}
+.f32 .va{background-position:0 -7424px;}
+.f32 .vc{background-position:0 -7456px;}
+.f32 .ve{background-position:0 -7488px;}
+.f32 .vg{background-position:0 -7520px;}
+.f32 .vi{background-position:0 -7552px;}
+.f32 .vn{background-position:0 -7584px;}
+.f32 .vu{background-position:0 -7616px;}
+.f32 .ws{background-position:0 -7648px;}
+.f32 .ye{background-position:0 -7680px;}
+.f32 .za{background-position:0 -7712px;}
+.f32 .zm{background-position:0 -7744px;}
+.f32 .zw{background-position:0 -7776px;}
+.f32 .sx{background-position:0 -7808px;}
+.f32 .cw{background-position:0 -7840px;}
+.f32 .ss{background-position:0 -7872px;}
+<<<<<<< 1fd2bdb43fc5cfdcf100cac8b72e67fd81e7f0fa
+.f32 .nu{background-position:0 -7904px;}
+=======
+.f32 .nu{background-position:0 -7904px;}
+>>>>>>> 0e0edb85a79343e4d020ff05378179e2323b21bd
diff --git a/backend/web/css/language.css b/backend/web/css/language.css
new file mode 100644
index 0000000..b2234a0
--- /dev/null
+++ b/backend/web/css/language.css
@@ -0,0 +1,89 @@
+section.content-header {
+ display: none;
+}
+section.content {
+ height: 100%;
+ padding: 0;
+}
+.content-wrapper {
+ background: #444444 !important;
+ position: absolute;
+ top: 50px;
+ height: calc(100% - 50px);
+ left:0;
+ width: calc(100% - 230px);
+}
+footer.main-footer {
+ position: absolute;
+ width: 100%;
+ bottom: 0;
+ left: 0;
+}
+footer.main-footer div.pull-right.hidden-xs {
+ margin-right: 230px;
+}
+.lang-column-1 {
+ float: left;
+ width: 50%;
+ height: 100%;
+ overflow: auto;
+}
+.lang-column-1 h1 {
+ text-align: center;
+ color: #ecf0f5;
+}
+.lang-column-2 {
+ background: #ecf0f5;
+ padding: 50px 10px 0 10px;
+ width: 50%;
+ height: 100%;
+ display:inline-block;
+}
+.settings_menu {
+ color: #ecf0f5;
+ white-space: normal !important;
+}
+.settings_menu a {
+ color: #ecf0f5;
+}
+.settings_menu a:focus {
+ color: #ecf0f5;
+}
+.settings_menu_inner {
+ list-style: none;
+ padding-left: 20px;
+ background: #2c3b41;
+}
+.settings_menu_inner li {
+ color: #8aa4af;
+ padding: 5px 0;
+}
+.settings_menu_inner li .grid-view {
+ background: #ecf0f5;
+ margin-right: 10px;
+ padding: 5px;
+ color: #8aa4af !important;
+}
+.settings_menu_inner li .grid-view a:hover, .settings_menu_inner li .grid-view a:focus {
+ color: #3C8DBC;
+}
+.settings_menu_inner li:hover {
+ color: #ecf0f5;
+ padding: 5px 0;
+}
+.settings_menu_inner li:focus {
+ color: #ecf0f5;
+ padding: 5px 0;
+}
+.settings_menu_inner li a {
+ color: #8aa4af;
+}
+.settings_menu_inner li a:hover {
+ color: #ecf0f5;
+}
+.settings_menu_inner li a:focus {
+ color: #ecf0f5;
+}
+.settings_menu_inner li a.glyphicon {
+ padding-left: 15px;
+}
\ No newline at end of file
diff --git a/backend/web/css/option.css b/backend/web/css/option.css
new file mode 100644
index 0000000..deeed60
--- /dev/null
+++ b/backend/web/css/option.css
@@ -0,0 +1,3 @@
+#main_row .remove_lang {
+ display: none;
+}
\ No newline at end of file
diff --git a/backend/web/css/site.css b/backend/web/css/site.css
new file mode 100644
index 0000000..7a12080
--- /dev/null
+++ b/backend/web/css/site.css
@@ -0,0 +1,135 @@
+html,
+body {
+ height: 100%;
+}
+
+.wrap {
+ min-height: 100%;
+ height: auto;
+ margin: 0 auto -60px;
+ padding: 0 0 60px;
+}
+
+.wrap > .container {
+ padding: 70px 15px 20px;
+}
+
+.footer {
+ height: 60px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #ddd;
+ padding-top: 20px;
+}
+
+.jumbotron {
+ text-align: center;
+ background-color: transparent;
+}
+
+.jumbotron .btn {
+ font-size: 21px;
+ padding: 14px 24px;
+}
+
+.not-set {
+ color: #c55;
+ font-style: italic;
+}
+
+/* add sorting icons to gridview sort links */
+a.asc:after, a.desc:after {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ padding-left: 5px;
+}
+
+a.asc:after {
+ content: /*"\e113"*/ "\e151";
+}
+
+a.desc:after {
+ content: /*"\e114"*/ "\e152";
+}
+
+.sort-numerical a.asc:after {
+ content: "\e153";
+}
+
+.sort-numerical a.desc:after {
+ content: "\e154";
+}
+
+.sort-ordinal a.asc:after {
+ content: "\e155";
+}
+
+.sort-ordinal a.desc:after {
+ content: "\e156";
+}
+
+.grid-view th {
+ white-space: nowrap;
+}
+
+.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;
+}
+.header-search {
+ width: 200px;
+}
+.skin-blue .sidebar-form {
+ margin: 5px !important;
+}
+.checkboxer .list-group-item {
+ padding: 0;
+}
+.checkboxer .list-group-item:hover {
+ background: #ddd;
+}
+.checkboxer .list-group-item.active, .checkboxer .list-group-item.active:hover {
+ background: #00a65a;
+}
+.checkboxer .list-group-item.active label, .checkboxer .list-group-item.active:hover label {
+ color: white;
+}
+.checkboxer .list-group-item.level0 {
+ padding-left: 15px;
+}
+.checkboxer .list-group-item.level1 {
+ padding-left: 45px;
+}
+.checkboxer .list-group-item.level2 {
+ padding-left: 75px;
+}
+.checkboxer .checkboxer_label {
+ padding: 10px 0;
+ display: block;
+}
+.checkboxer .checkboxer_label input[type=radio] {
+ position: absolute;
+ clip: rect(0,0,0,0);
+}
+.action_link {
+ float: left;
+}
+.admin_grid_action {
+ padding: 0 20px;
+}
+.admin_grid_action .pagination {
+ margin: 0;
+}
diff --git a/backend/web/favicon.ico b/backend/web/favicon.ico
new file mode 100644
index 0000000..580ed73
Binary files /dev/null and b/backend/web/favicon.ico differ
diff --git a/backend/web/images/flags32.png b/backend/web/images/flags32.png
new file mode 100644
index 0000000..72e9535
Binary files /dev/null and b/backend/web/images/flags32.png differ
diff --git a/backend/web/js/language.js b/backend/web/js/language.js
new file mode 100644
index 0000000..131b298
--- /dev/null
+++ b/backend/web/js/language.js
@@ -0,0 +1,6 @@
+$(function() {
+ $(document).on('click', '.settings_menu>li>a', function() {
+ console.log('event');
+ $(this).parent().toggleClass('active');
+ });
+});
\ No newline at end of file
diff --git a/backend/web/js/option.js b/backend/web/js/option.js
new file mode 100644
index 0000000..f0e930a
--- /dev/null
+++ b/backend/web/js/option.js
@@ -0,0 +1,133 @@
+function readURL(input) {
+ $(input).parents('.tab-pane').find('.image_inputs_prev').remove();
+ var urls = [];
+ if (input.files) {
+ $.each(input.files, function(key, value) {
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ $(input).parent().append(' ');
+ }
+ reader.readAsDataURL(value);
+ });
+ }
+ return urls;
+}
+function checkboxerInit() {
+ $.each($('.checkboxer input[type=radio]:checked'), function(index, value) {
+ $(value).trigger('change');
+ });
+}
+$(function() {
+ var counter = 0;
+ $(document).on('click', '.add_row', function() {
+ counter++;
+ var clone = $('#main_row').clone().html().replace(new RegExp("Option\\[0\\]", 'g'), "Option["+counter+"]");
+ console.log(form);
+ $(clone).appendTo('#'+form);
+ $('#'+form+' button[type=submit]').parent().appendTo('#'+form);
+ });
+ $(document).on('click', '.add_lang', function() {
+ var field_block = $(this).parent().parent();
+ if($(this).hasClass('active')) {
+ $(field_block).find('.main_input').attr('required', '').show();
+ $(field_block).find('.lang_inputs').hide();
+ $(this).removeClass('active');
+ } else {
+ $(field_block).find('.main_input').removeAttr('required').hide();
+ $(field_block).find('.lang_inputs').show();
+ $(this).addClass('active');
+ }
+ });
+ $(document).on('click', '.remove_lang', function() {
+ $(this).parents('.form-wrapper').remove();
+ });
+ $(document).on('change', '.image_inputs_field', function() {
+ readURL(this);
+ });
+ $('a.remove_image').on('click', function(e) {
+ var el = $(this);
+ e.preventDefault();
+ if(confirm(confirm_message)) {
+ $.ajax({
+ type: 'post',
+ url: $(this).attr('href'),
+ data: $(this).data('params')
+ }).done(function() {
+ $(el).parents('.additional_image_container').remove();
+ });
+ }
+ return false;
+ });
+ $.each($('.nav-tabs.f32'), function(key, value) {
+ if($(value).find('li').length > 1) {
+ $(value).find('li').append(' ');
+ }
+ });
+ $(document).on('click', '.dropdown-menu.f32:not(.old) li a[data-lang]', function() {
+ var lang = $(this).data('lang');
+ var flag = $(this).find('span').first().clone();
+ var el = $(this);
+ var id = $(this).attr('href').substr(1);
+ var path = form[id].handler;
+ var view = form[id].view;
+ var model = form[id].model;
+ $.get(path, { language_id: lang, widget_id: id, ajaxView: view, model: model }, function(data) {
+ $('#'+id+'-tabs li').removeClass('active');
+ $('#'+id+'-tabs').append(''+$('').append($(flag)).html()+'
');
+ $('#tab-content-'+id+' .tab-pane.active').removeClass('active');
+ $('#tab-content-'+id).append($(data).find('.ajax-loaded').first());
+ $('body').append($(data).filter('script'));
+ $(el).parent().remove();
+ if(!$('#lang-'+id+' li').length) {
+ $('#'+id+'Lang').addClass('disabled');
+ }
+ if($('#'+id+'-tabs li').length > 1) {
+ $('#'+id+'-tabs li').append(' ')
+ }
+ });
+ });
+ $(document).on('click', '.dropdown-menu.f32.old li a[data-lang]', function(e) {
+ e.preventDefault();
+ var lang = $(this).data('lang');
+ var flag = $(this).find('span').first().clone();
+ var el = $(this);
+ var id = $(this).attr('href').substr(1);
+ $.get(form[id], { language_id: lang, widget_id: id }, function(data) {
+ $('#'+id+'-tabs li').removeClass('active');
+ $('#'+id+'-tabs').append(''+$('').append($(flag)).html()+'
');
+ $('#tab-content-'+id+' .tab-pane.active').removeClass('active');
+ $('#tab-content-'+id).append($(data).find('.ajax-loaded').first());
+ $('body').append($(data).filter('script'));
+ $(el).parent().remove();
+ if(!$('#lang-'+id+' li').length) {
+ $('#'+id+'Lang').addClass('disabled');
+ }
+ if($('#'+id+'-tabs li').length > 1) {
+ $('#'+id+'-tabs li').append(' ')
+ }
+ });
+ });
+ $(document).on('click', '.remove-lang', function() {
+ var lang = $(this).parent().data('lang');
+ var flag = $(this).parent().find('span.flag').first().clone();
+ var id = $(this).parent().find('a[aria-controls]').first().attr('aria-controls').substr(0,8);
+ $('#'+id+'-'+lang).remove();
+ $('#lang-'+id).append(''+$('').append($(flag)).html()+'
');
+ $('#'+id+'Lang').removeClass('disabled');
+ $(this).parent().remove();
+ if($('#'+id+'-tabs li').length <= 1) {
+ $('#'+id+'-tabs li').find('.remove-lang').remove();
+ }
+ if(!$('#'+id+'-tabs>li.active').length) {
+ $('#'+id+'-tabs>li').first().find('a').tab('show');
+ }
+ });
+ $(document).on('change', '.checkboxer .checkboxer_label input[type=radio]', function() {
+ $(this).parents('.checkboxer').find('.checkboxer_container').removeClass('active');
+ $(this).parents('.checkboxer_container').addClass('active');
+ });
+ $.each($('.f32'), function(i, val) {
+ $(val).find('a[role=tab]').first().trigger('click');
+ });
+ checkboxerInit();
+});
\ No newline at end of file
diff --git a/backend/web/robots.txt b/backend/web/robots.txt
new file mode 100644
index 0000000..1f53798
--- /dev/null
+++ b/backend/web/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/common/components/LangRequest.php b/common/components/LangRequest.php
new file mode 100644
index 0000000..9b5ab3f
--- /dev/null
+++ b/common/components/LangRequest.php
@@ -0,0 +1,99 @@
+_lang_url === null)
+ {
+ $this->_lang_url = $this->getUrl();
+
+ $url_list = explode ('/', $this->_lang_url);
+
+ $lang_url = isset ($url_list[1]) ? $url_list[1] : null;
+
+ Language::setCurrent($lang_url);
+
+ if ($lang_url !== null && $lang_url === Language::getCurrent()->language_code
+ && strpos($this->_lang_url, Language::getCurrent()->language_code) === 1)
+ {
+ $this->_lang_url = substr ($this->_lang_url, strlen (Language::getCurrent()->language_code) + 1);
+ }
+ }
+
+ return $this->_lang_url;
+ }
+
+ protected function resolvePathInfo()
+ {
+ $pathInfo = $this->getLangUrl();
+
+ if (($pos = strpos ($pathInfo, '?')) !== false)
+ {
+ $pathInfo = substr ($pathInfo, 0, $pos);
+ }
+
+ $pathInfo = urldecode ($pathInfo);
+
+ // try to encode in UTF8 if not so
+ // http://w3.org/International/questions/qa-forms-utf-8.html
+ if (! preg_match ('%^(?:
+ [\x09\x0A\x0D\x20-\x7E] # ASCII
+ | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
+ | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
+ | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
+ | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
+ | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
+ | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
+ )*$%xs', $pathInfo)
+ )
+ {
+ $pathInfo = utf8_encode($pathInfo);
+ }
+
+ $scriptUrl = $this->getScriptUrl();
+
+ $baseUrl = $this->getBaseUrl();
+
+ if (strpos($pathInfo, $scriptUrl) === 0)
+ {
+ $pathInfo = substr($pathInfo, strlen($scriptUrl));
+ }
+ else if ($baseUrl === '' || strpos($pathInfo, $baseUrl) === 0)
+ {
+ $pathInfo = substr($pathInfo, strlen($baseUrl));
+ }
+ elseif (isset ($_SERVER['PHP_SELF']) && strpos ($_SERVER['PHP_SELF'], $scriptUrl) === 0)
+ {
+ $pathInfo = substr($_SERVER['PHP_SELF'], strlen($scriptUrl));
+ }
+ else
+ {
+ throw new InvalidConfigException('Unable to determine the path info of the current request.');
+ }
+
+ if ($pathInfo[0] === '/')
+ {
+ $pathInfo = substr ($pathInfo, 1);
+ }
+
+ return (string) $pathInfo;
+ }
+}
\ No newline at end of file
diff --git a/common/components/LangUrlManager.php b/common/components/LangUrlManager.php
new file mode 100644
index 0000000..2170b02
--- /dev/null
+++ b/common/components/LangUrlManager.php
@@ -0,0 +1,37 @@
+is_default == 1 ? $url : '/'.$lang_code->lang_code.$url);
+ }
+}
\ No newline at end of file
diff --git a/common/components/Request.php b/common/components/Request.php
new file mode 100644
index 0000000..f3a4f37
--- /dev/null
+++ b/common/components/Request.php
@@ -0,0 +1,40 @@
+web, "", parent::getBaseUrl()) . $this->adminUrl;
+
+ }
+
+
+
+ public function resolvePathInfo()
+
+ {
+
+ if ($this->getUrl() === $this->adminUrl) {
+
+ return "";
+
+ } else {
+
+ return parent::resolvePathInfo();
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/LICENSE b/common/components/developeruz/db_rbac/LICENSE
new file mode 100644
index 0000000..bc2f342
--- /dev/null
+++ b/common/components/developeruz/db_rbac/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Elvira Sheina
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/common/components/developeruz/db_rbac/README.md b/common/components/developeruz/db_rbac/README.md
new file mode 100644
index 0000000..5cb0a09
--- /dev/null
+++ b/common/components/developeruz/db_rbac/README.md
@@ -0,0 +1,169 @@
+Динамическая настройка прав доступа для Yii2
+============
+
+Модуль для создания ролей и прав доступа через веб-интерфейс, так же имеющий веб интерфейс для назначения ролей пользователям
+Поведение для приложения, проверяющее право доступа к action по внесенным в модуле правилам.
+
+###Установка:###
+```bash
+$ php composer.phar require developeruz/yii2-db-rbac "*"
+```
+
+Для корректной работы модуля необходимо настроить authManager в конфиге приложения (common/config/main.php для advanced или config/web.php и config/console для basic приложения)
+```php
+ 'components' => [
+ 'authManager' => [
+ 'class' => 'yii\rbac\DbManager',
+ ],
+ ...
+ ]
+```
+
+И выполнить миграции, создающие таблицы для DbManager (подразумевается, что коннект к БД для приложения уже настроен)
+```bash
+$ yii migrate --migrationPath=@yii/rbac/migrations/
+```
+
+##Подключение модуля##
+В конфиге приложения (backend/config/main.php для advanced или config/web.php для basic приложения) прописываем модуль
+```php
+ 'modules' => [
+ 'permit' => [
+ 'class' => 'developeruz\db_rbac\Yii2DbRbac',
+ ],
+ ],
+```
+Если нужно передать layout это можно сделать так:
+```php
+ 'modules' => [
+ 'permit' => [
+ 'class' => 'developeruz\db_rbac\Yii2DbRbac',
+ 'layout' => '//admin'
+ ],
+ ],
+```
+
+Если вы используете ЧПУ, то убедитесь что у вас прописаны правила роутинга для модулей
+```php
+'//' => '//',
+'///' => '//',
+```
+
+**Добавляем ссылки в меню**
+
+**/permit/access/role - управление ролями**
+
+**/permit/access/permission - управление правами доступа**
+
+###Назначение ролей пользователям###
+По многочисленным просьбам в модуль добавлен интерфейс для назначения ролей пользователям.
+
+Для корректной работы модуля нужно указать в параметрах модуля класс `User`.
+```php
+'modules' => [
+ 'permit' => [
+ 'class' => 'app\modules\db_rbac\Yii2DbRbac',
+ 'params' => [
+ 'userClass' => 'app\models\User'
+ ]
+ ],
+ ],
+```
+
+Класс User должен реализовывать интерфейс `developeruz\db_rbac\interfaces\UserRbacInterface`.
+В большинстве случаев придется дописать в нем 1 функцию `getUserName()` которая будет возвращать отображаемое имя пользователя.
+```php
+use developeruz\db_rbac\interfaces\UserRbacInterface;
+
+class User extends ActiveRecord implements IdentityInterface, UserRbacInterface
+{
+...
+ public function getUserName()
+ {
+ return $this->username;
+ }
+}
+```
+
+**Управление ролью пользователя происходит на странице `/permit/user/view/1` для пользователя с id=1.**
+Удобнее всего дописать кнопку на эту страницу в Grid со списком пользователей.
+```php
+echo GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'id',
+ 'username',
+ 'email:email',
+
+ ['class' => 'yii\grid\ActionColumn',
+ 'template' => '{view} {update} {permit} {delete}',
+ 'buttons' =>
+ [
+ 'permit' => function ($url, $model) {
+ return Html::a(' ', Url::to(['/permit/user/view', 'id' => $model->id]), [
+ 'title' => Yii::t('yii', 'Change user role')
+ ]); },
+ ]
+ ],
+ ],
+]);
+```
+
+Присвоить роль пользователю можно и в коде, например при создании нового пользователя.
+```php
+$userRole = Yii::$app->authManager->getRole('name_of_role');
+Yii::$app->authManager->assign($userRole, $user->getId());
+```
+
+Проверить, имеет ли пользователь право на действие можно через метод `can()` компонента User
+```php
+Yii::$app->user->can($permissionName);
+```
+$permissionName - может быть как ролью так и правом
+
+##Поведение, динамически проверяющее наличие прав##
+
+Данное поведение позволяет не писать Yii::$app->user->can($permissionName); в каждом action, а проверять права доступа на лету.
+Это удобно для гибкой настройки прав при использовании сторонних модулей.
+
+###Подключение поведения###
+В конфиге того приложения, доступ к которому следует проверять на лету, необходимо подключить поведение
+```php
+use developeruz\db_rbac\behaviors\AccessBehavior;
+
+ 'as AccessBehavior' => [
+ 'class' => AccessBehavior::className(),
+ ]
+```
+С этого момента, после обработки запроса (событие EVENT_AFTER_REQUEST) проверяются права текущего пользователя (Yii::$app->user) на выполнение запрашиваемого действия (Yii::$app->user->can())
+Действие считается разрешенным, если:
+ - пользователю разрешен доступ к конкретному action (правило записано как: module/controller/action)
+ - пользователю разрешен доступ к любым action данного контроллера (правило записано как: module/controller)
+ - пользователю разрешен доступ к любым action данного модуля (правило записано как: module)
+
+###Настройка прав доступа по умолчанию###
+После подключения поведения, доступ становится возможен только авторизованному пользователю, имеющему некие права.
+Для исключений из этого правила можно прописать доступы по умолчанию в том же формате AccessControl, что и в контроллере:
+```php
+ 'as AccessBehavior' => [
+ 'class' => AccessBehavior::className(),
+ 'rules' =>
+ ['site' =>
+ [
+ [
+ 'actions' => ['login', 'index'],
+ 'allow' => true,
+ ],
+ [
+ 'actions' => ['about'],
+ 'allow' => true,
+ 'roles' => ['admin'],
+ ],
+ ]
+ ]
+ ]
+```
+В приведенном выше примере разрешен доступ любому пользователю к site/login и site/index и доступ пользователя с ролью admin к site/about
+Правила прописанные в конфиге имеют приоритет над динамически настраиваемыми правилами.
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/Yii2DbRbac.php b/common/components/developeruz/db_rbac/Yii2DbRbac.php
new file mode 100644
index 0000000..a3dba7b
--- /dev/null
+++ b/common/components/developeruz/db_rbac/Yii2DbRbac.php
@@ -0,0 +1,38 @@
+
+ * @version 0.1
+ * @package Yii2DbRbac for Yii2
+ *
+ */
+namespace common\components\developeruz\db_rbac;
+
+use Yii;
+
+class Yii2DbRbac extends \yii\base\Module
+{
+ public $controllerNamespace = 'common\components\developeruz\db_rbac\controllers';
+ public $userClass;
+
+ public function init()
+ {
+ parent::init();
+ $this->registerTranslations();
+ }
+
+ public function registerTranslations()
+ {
+ Yii::$app->i18n->translations['db_rbac'] = [
+ 'class' => 'yii\i18n\PhpMessageSource',
+ 'sourceLanguage' => 'ru-Ru',
+ 'basePath' => '@developeruz/db_rbac/messages',
+ ];
+ }
+
+ public static function t($category, $message, $params = [], $language = null)
+ {
+ return Yii::t('modules/db_rbac/' . $category, $message, $params, $language);
+ }
+}
diff --git a/common/components/developeruz/db_rbac/behaviors/AccessBehavior.php b/common/components/developeruz/db_rbac/behaviors/AccessBehavior.php
new file mode 100644
index 0000000..1489e4a
--- /dev/null
+++ b/common/components/developeruz/db_rbac/behaviors/AccessBehavior.php
@@ -0,0 +1,103 @@
+
+ * @version 0.1
+ * @package AccessBehavior for Yii2
+ *
+ */
+namespace developeruz\db_rbac\behaviors;
+
+use Yii;
+use yii\behaviors\AttributeBehavior;
+use yii\di\Instance;
+use yii\base\Module;
+use yii\web\Application;
+use yii\web\User;
+use yii\filters\AccessControl;
+use yii\web\ForbiddenHttpException;
+
+class AccessBehavior extends AttributeBehavior {
+
+ public $rules=[];
+
+ private $_rules = [];
+
+ public function events()
+ {
+ return [
+ Module::EVENT_BEFORE_ACTION => 'interception',
+ ];
+ }
+
+ public function interception($event)
+ {
+ if(!isset( Yii::$app->i18n->translations['db_rbac'])){
+ Yii::$app->i18n->translations['db_rbac'] = [
+ 'class' => 'yii\i18n\PhpMessageSource',
+ 'sourceLanguage' => 'ru-Ru',
+ 'basePath' => '@developeruz/db_rbac/messages',
+ ];
+ }
+
+ $route = Yii::$app->getRequest()->resolve();
+
+ //Проверяем права по конфигу
+ $this->createRule();
+ $user = Instance::ensure(Yii::$app->user, User::className());
+ $request = Yii::$app->getRequest();
+ $action = $event->action;
+
+ if(!$this->cheсkByRule($action, $user, $request))
+ {
+ //И по AuthManager
+ if(!$this->checkPermission($route))
+ throw new ForbiddenHttpException(Yii::t('db_rbac','Недостаточно прав'));
+ }
+ }
+
+ protected function createRule()
+ {
+ foreach($this->rules as $controller => $rule)
+ {
+ foreach ($rule as $singleRule) {
+ if (is_array($singleRule)) {
+ $option = [
+ 'controllers' => [$controller],
+ 'class' => 'yii\filters\AccessRule'
+ ];
+ $this->_rules[] = Yii::createObject(array_merge($option, $singleRule));
+ }
+ }
+ }
+ }
+
+ protected function cheсkByRule($action, $user, $request)
+ {
+ foreach ($this->_rules as $rule) {
+ if ($rule->allows($action, $user, $request))
+ return true;
+ }
+ return false;
+ }
+
+ protected function checkPermission($route)
+ {
+ //$route[0] - is the route, $route[1] - is the associated parameters
+
+ $routePathTmp = explode('/', $route[0]);
+ $routeVariant = array_shift($routePathTmp);
+ if(Yii::$app->user->can($routeVariant, $route[1]))
+ return true;
+
+ foreach($routePathTmp as $routePart)
+ {
+ $routeVariant .= '/'.$routePart;
+ if(Yii::$app->user->can($routeVariant, $route[1]))
+ return true;
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/composer.json b/common/components/developeruz/db_rbac/composer.json
new file mode 100644
index 0000000..20b7fc1
--- /dev/null
+++ b/common/components/developeruz/db_rbac/composer.json
@@ -0,0 +1,25 @@
+{
+ "name": "developeruz/yii2-db-rbac",
+ "description": "Dynamic control of access rights in YII2",
+ "keywords": ["yii", "rbac"],
+ "type": "yii2-extension",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Elvira Sheina",
+ "email": "elleuz@gmail.com",
+ "homepage": "http://developer.uz"
+ }
+ ],
+ "require": {
+ "yiisoft/yii2": "*"
+ },
+ "support": {
+ "issues": "https://github.com/developeruz/yii2-db-rbac/issues"
+ },
+ "autoload": {
+ "psr-4": {
+ "developeruz\\db_rbac\\": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/controllers/AccessController.php b/common/components/developeruz/db_rbac/controllers/AccessController.php
new file mode 100644
index 0000000..0547f26
--- /dev/null
+++ b/common/components/developeruz/db_rbac/controllers/AccessController.php
@@ -0,0 +1,238 @@
+
+ * @version 0.1
+ * @package AccessController for Yii2
+ *
+ */
+namespace common\components\developeruz\db_rbac\controllers;
+
+use Yii;
+use yii\web\Controller;
+use yii\web\BadRequestHttpException;
+use yii\rbac\Role;
+use yii\rbac\Permission;
+use yii\helpers\ArrayHelper;
+use yii\helpers\Url;
+use yii\validators\RegularExpressionValidator;
+
+class AccessController extends Controller
+{
+ protected $error;
+ protected $pattern4Role = '/^[a-zA-Z0-9_-]+$/';
+ protected $pattern4Permission = '/^[a-zA-Z0-9_\/-]+$/';
+
+ public function actions()
+ {
+ return [
+ 'error' => [
+ 'class' => 'yii\web\ErrorAction',
+ ],
+ ];
+ }
+
+ public function actionRole()
+ {
+ return $this->render('role');
+ }
+
+ public function actionAddRole()
+ {
+ if (Yii::$app->request->post('name')
+ && $this->validate(Yii::$app->request->post('name'), $this->pattern4Role)
+ && $this->isUnique(Yii::$app->request->post('name'), 'role')
+ ) {
+ $role = Yii::$app->authManager->createRole(Yii::$app->request->post('name'));
+ $role->description = Yii::$app->request->post('description');
+ Yii::$app->authManager->add($role);
+ $this->setPermissions(Yii::$app->request->post('permissions', []), $role);
+ return $this->redirect(Url::toRoute([
+ 'update-role',
+ 'name' => $role->name
+ ]));
+ }
+
+ $permissions = ArrayHelper::map(Yii::$app->authManager->getPermissions(), 'name', 'description');
+ return $this->render(
+ 'addRole',
+ [
+ 'permissions' => $permissions,
+ 'error' => $this->error
+ ]
+ );
+ }
+
+ public function actionUpdateRole($name)
+ {
+ $role = Yii::$app->authManager->getRole($name);
+
+ $permissions = ArrayHelper::map(Yii::$app->authManager->getPermissions(), 'name', 'description');
+ $role_permit = array_keys(Yii::$app->authManager->getPermissionsByRole($name));
+
+ if ($role instanceof Role) {
+ if (Yii::$app->request->post('name')
+ && $this->validate(Yii::$app->request->post('name'), $this->pattern4Role)
+ ) {
+ if (Yii::$app->request->post('name') != $name && !$this->isUnique(Yii::$app->request->post('name'), 'role')) {
+ return $this->render(
+ 'updateRole',
+ [
+ 'role' => $role,
+ 'permissions' => $permissions,
+ 'role_permit' => $role_permit,
+ 'error' => $this->error
+ ]
+ );
+ }
+ $role = $this->setAttribute($role, Yii::$app->request->post());
+ Yii::$app->authManager->update($name, $role);
+ Yii::$app->authManager->removeChildren($role);
+ $this->setPermissions(Yii::$app->request->post('permissions', []), $role);
+ return $this->redirect(Url::toRoute([
+ 'update-role',
+ 'name' => $role->name
+ ]));
+ }
+
+ return $this->render(
+ 'updateRole',
+ [
+ 'role' => $role,
+ 'permissions' => $permissions,
+ 'role_permit' => $role_permit,
+ 'error' => $this->error
+ ]
+ );
+ } else {
+ throw new BadRequestHttpException(Yii::t('db_rbac', 'Страница не найдена'));
+ }
+ }
+
+ public function actionDeleteRole($name)
+ {
+ $role = Yii::$app->authManager->getRole($name);
+ if ($role) {
+ Yii::$app->authManager->removeChildren($role);
+ Yii::$app->authManager->remove($role);
+ }
+ return $this->redirect(Url::toRoute(['role']));
+ }
+
+
+ public function actionPermission()
+ {
+ return $this->render('permission');
+ }
+
+ public function actionAddPermission()
+ {
+ $permission = $this->clear(Yii::$app->request->post('name'));
+ if ($permission
+ && $this->validate($permission, $this->pattern4Permission)
+ && $this->isUnique($permission, 'permission')
+ ) {
+ $permit = Yii::$app->authManager->createPermission($permission);
+ $permit->description = Yii::$app->request->post('description', '');
+ Yii::$app->authManager->add($permit);
+ return $this->redirect(Url::toRoute([
+ 'update-permission',
+ 'name' => $permit->name
+ ]));
+ }
+
+ return $this->render('addPermission', ['error' => $this->error]);
+ }
+
+ public function actionUpdatePermission($name)
+ {
+ $permit = Yii::$app->authManager->getPermission($name);
+ if ($permit instanceof Permission) {
+ $permission = $this->clear(Yii::$app->request->post('name'));
+ if ($permission && $this->validate($permission, $this->pattern4Permission)
+ ) {
+ if($permission!= $name && !$this->isUnique($permission, 'permission'))
+ {
+ return $this->render('updatePermission', [
+ 'permit' => $permit,
+ 'error' => $this->error
+ ]);
+ }
+
+ $permit->name = $permission;
+ $permit->description = Yii::$app->request->post('description', '');
+ Yii::$app->authManager->update($name, $permit);
+ return $this->redirect(Url::toRoute([
+ 'update-permission',
+ 'name' => $permit->name
+ ]));
+ }
+
+ return $this->render('updatePermission', [
+ 'permit' => $permit,
+ 'error' => $this->error
+ ]);
+ } else throw new BadRequestHttpException(Yii::t('db_rbac', 'Страница не найдена'));
+ }
+
+ public function actionDeletePermission($name)
+ {
+ $permit = Yii::$app->authManager->getPermission($name);
+ if ($permit)
+ Yii::$app->authManager->remove($permit);
+ return $this->redirect(Url::toRoute(['permission']));
+ }
+
+ protected function setAttribute($object, $data)
+ {
+ $object->name = $data['name'];
+ $object->description = $data['description'];
+ return $object;
+ }
+
+ protected function setPermissions($permissions, $role)
+ {
+ foreach ($permissions as $permit) {
+ $new_permit = Yii::$app->authManager->getPermission($permit);
+ Yii::$app->authManager->addChild($role, $new_permit);
+ }
+ }
+
+ protected function validate($field, $regex)
+ {
+ $validator = new RegularExpressionValidator(['pattern' => $regex]);
+ if ($validator->validate($field, $error))
+ return true;
+ else {
+ $this->error[] = Yii::t('db_rbac', 'Значение "{field}" содержит не допустимые символы', ['field' => $field]);
+ return false;
+ }
+ }
+
+ protected function isUnique($name, $type)
+ {
+ if ($type == 'role') {
+ $role = Yii::$app->authManager->getRole($name);
+ if ($role instanceof Role) {
+ $this->error[] = Yii::t('db_rbac', 'Роль с таким именем уже существует: ') . $name;
+ return false;
+ } else return true;
+ } elseif ($type == 'permission') {
+ $permission = Yii::$app->authManager->getPermission($name);
+ if ($permission instanceof Permission) {
+ $this->error[] = Yii::t('db_rbac', 'Правило с таким именем уже существует: ') . $name;
+ return false;
+ } else return true;
+ }
+ }
+
+ protected function clear($value)
+ {
+ if (!empty($value)) {
+ $value = trim($value, "/ \t\n\r\0\x0B");
+ }
+
+ return $value;
+ }
+}
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/controllers/UserController.php b/common/components/developeruz/db_rbac/controllers/UserController.php
new file mode 100644
index 0000000..c067913
--- /dev/null
+++ b/common/components/developeruz/db_rbac/controllers/UserController.php
@@ -0,0 +1,100 @@
+
+ * @version 0.1
+ * @package UserController for Yii2
+ *
+ */
+namespace common\components\developeruz\db_rbac\controllers;
+
+use Yii;
+use yii\filters\VerbFilter;
+use yii\helpers\ArrayHelper;
+use yii\helpers\Url;
+use yii\web\Controller;
+use yii\web\BadRequestHttpException;
+use developeruz\db_rbac\interfaces\UserRbacInterface;
+use yii\web\NotFoundHttpException;
+
+class UserController extends Controller
+{
+ public $moduleName = 'permit';
+
+ public function beforeAction($action)
+ {
+ if(empty(Yii::$app->controller->module->params['userClass'])){
+ throw new BadRequestHttpException(Yii::t('db_rbac','Необходимо указать класс User в настройках модуля'));
+ }
+
+ $user = new Yii::$app->controller->module->params['userClass']();
+
+ if(! $user instanceof UserRbacInterface)
+ {
+ throw new BadRequestHttpException(Yii::t('db_rbac', 'UserClass должен реализовывать интерфейс developeruz\db_rbac\UserRbacInterface'));
+ }
+
+ return parent::beforeAction($action);
+ }
+
+ public function actions()
+ {
+ return [
+ 'error' => [
+ 'class' => 'yii\web\ErrorAction',
+ ],
+ ];
+ }
+
+ public function behaviors()
+ {
+ return [
+ 'verbs' => [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'update' => ['post'],
+ '*' => ['get'],
+ ],
+ ],
+ ];
+ }
+
+ public function actionView($id)
+ {
+ $roles = ArrayHelper::map(Yii::$app->authManager->getRoles(), 'name', 'description');
+ $user_permit = array_keys(Yii::$app->authManager->getRolesByUser($id));
+ $user = $this->findUser($id);
+ return $this->render('view', [
+ 'user' => $user,
+ 'roles' => $roles,
+ 'user_permit' => $user_permit,
+ 'moduleName' => Yii::$app->controller->module->id
+ ]);
+ }
+
+ public function actionUpdate($id)
+ {
+ $user = $this->findUser($id);
+ Yii::$app->authManager->revokeAll($user->getId());
+ if(Yii::$app->request->post('roles')){
+ foreach(Yii::$app->request->post('roles') as $role)
+ {
+ $new_role = Yii::$app->authManager->getRole($role);
+ Yii::$app->authManager->assign($new_role, $user->getId());
+ }
+ }
+ return $this->redirect(Url::to(["/".Yii::$app->controller->module->id."/user/view", 'id' => $user->getId()]));
+ }
+
+ private function findUser($id)
+ {
+ $class = new Yii::$app->controller->module->params['userClass']();
+ $user = $class::findIdentity($id);
+ if(empty($user)){
+ throw new NotFoundHttpException(Yii::t('db_rbac', 'Пользователь не найден'));
+ } else {
+ return $user;
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/interfaces/UserRbacInterface.php b/common/components/developeruz/db_rbac/interfaces/UserRbacInterface.php
new file mode 100644
index 0000000..d19bf2e
--- /dev/null
+++ b/common/components/developeruz/db_rbac/interfaces/UserRbacInterface.php
@@ -0,0 +1,10 @@
+ 'Permission with the same name already exists: ',
+ 'Роль с таким именем уже существует: ' => 'Role with the same name already exists: ',
+ 'Значение "{field}" содержит недопустимые символы' => '"{field}" value contains invalid characters',
+ 'Страница не найдена' => 'Page not found',
+ 'Недостаточно прав' => 'You not allow to access',
+ 'Необходимо указать класс User в настройках модуля' => 'UserClass params must be set in config file',
+ 'UserClass должен реализовывать интерфейс developeruz\db_rbac\UserRbacInterface' => 'UserClass must implements developeruz\db_rbac\UserRbacInterface',
+ 'Пользователь не найден' => 'User not found'
+];
diff --git a/common/components/developeruz/db_rbac/views/access/addPermission.php b/common/components/developeruz/db_rbac/views/access/addPermission.php
new file mode 100644
index 0000000..7f79870
--- /dev/null
+++ b/common/components/developeruz/db_rbac/views/access/addPermission.php
@@ -0,0 +1,55 @@
+title = Yii::t('db_rbac', 'Новое правило');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('db_rbac', 'Правила доступа'), 'url' => ['permission']];
+$this->params['breadcrumbs'][] = Yii::t('db_rbac', 'Новое правило');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+
diff --git a/common/components/developeruz/db_rbac/views/access/addRole.php b/common/components/developeruz/db_rbac/views/access/addRole.php
new file mode 100644
index 0000000..4730c0f
--- /dev/null
+++ b/common/components/developeruz/db_rbac/views/access/addRole.php
@@ -0,0 +1,57 @@
+title = Yii::t('db_rbac', 'Новая роль');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('db_rbac', 'Управление ролями'), 'url' => ['role']];
+$this->params['breadcrumbs'][] = 'Новая роль';
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/views/access/permission.php b/common/components/developeruz/db_rbac/views/access/permission.php
new file mode 100644
index 0000000..664badd
--- /dev/null
+++ b/common/components/developeruz/db_rbac/views/access/permission.php
@@ -0,0 +1,69 @@
+title = Yii::t('db_rbac', 'Правила доступа');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('db_rbac', 'Добавить новое правило'), ['add-permission'], ['class' => 'btn btn-success']) ?>
+
+ Yii::$app->authManager->getPermissions(),
+ 'sort' => [
+ 'attributes' => ['name', 'description'],
+ ],
+ 'pagination' => [
+ 'pageSize' => 10,
+ ],
+ ]);
+?>
+
+=GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ [
+ 'class' => DataColumn::className(),
+ 'attribute' => 'name',
+ 'label' => Yii::t('db_rbac', 'Правило')
+ ],
+ [
+ 'class' => DataColumn::className(),
+ 'attribute' => 'description',
+ 'label' => Yii::t('db_rbac', 'Описание')
+ ],
+ ['class' => 'yii\grid\ActionColumn',
+ 'template' => '{update} {delete}',
+ 'buttons' =>
+ [
+ 'update' => function ($url, $model) {
+ return Html::a('
', Url::toRoute(['update-permission', 'name' => $model->name]), [
+ 'title' => Yii::t('yii', 'Update'),
+ 'data-pjax' => '0',
+ ]); },
+ 'delete' => function ($url, $model) {
+ return Html::a('
', Url::toRoute(['delete-permission','name' => $model->name]), [
+ 'title' => Yii::t('yii', 'Delete'),
+ 'data-confirm' => Yii::t('yii', 'Are you sure you want to delete this item?'),
+ 'data-method' => 'post',
+ 'data-pjax' => '0',
+ ]);
+ }
+ ]
+ ],
+ ]
+ ]);
+?>
+
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/views/access/role.php b/common/components/developeruz/db_rbac/views/access/role.php
new file mode 100644
index 0000000..f5bef67
--- /dev/null
+++ b/common/components/developeruz/db_rbac/views/access/role.php
@@ -0,0 +1,76 @@
+title = Yii::t('db_rbac', 'Управление ролями');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('db_rbac', 'Добавить роль'), ['add-role'], ['class' => 'btn btn-success']) ?>
+
+ Yii::$app->authManager->getRoles(),
+ 'sort' => [
+ 'attributes' => ['name', 'description'],
+ ],
+ 'pagination' => [
+ 'pageSize' => 10,
+ ],
+ ]);
+?>
+
+=GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ [
+ 'class' => DataColumn::className(),
+ 'attribute' => 'name',
+ 'label' => Yii::t('db_rbac', 'Роль')
+ ],
+ [
+ 'class' => DataColumn::className(),
+ 'attribute' => 'description',
+ 'label' => Yii::t('db_rbac', 'Описание')
+ ],
+ [
+ 'class' => DataColumn::className(),
+ 'label' => Yii::t('db_rbac', 'Разрешенные доступы'),
+ 'format' => ['html'],
+ 'value' => function($data) { return implode('
',array_keys(ArrayHelper::map(Yii::$app->authManager->getPermissionsByRole($data->name), 'description', 'description')));}
+ ],
+ ['class' => 'yii\grid\ActionColumn',
+ 'template' => '{update} {delete}',
+ 'buttons' =>
+ [
+ 'update' => function ($url, $model) {
+ return Html::a('
', Url::toRoute(['update-role', 'name' => $model->name]), [
+ 'title' => Yii::t('yii', 'Update'),
+ 'data-pjax' => '0',
+ ]); },
+ 'delete' => function ($url, $model) {
+ return Html::a('
', Url::toRoute(['delete-role','name' => $model->name]), [
+ 'title' => Yii::t('yii', 'Delete'),
+ 'data-confirm' => Yii::t('yii', 'Are you sure you want to delete this item?'),
+ 'data-method' => 'post',
+ 'data-pjax' => '0',
+ ]);
+ }
+ ]
+ ],
+ ]
+ ]);
+?>
+
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/views/access/updatePermission.php b/common/components/developeruz/db_rbac/views/access/updatePermission.php
new file mode 100644
index 0000000..076275a
--- /dev/null
+++ b/common/components/developeruz/db_rbac/views/access/updatePermission.php
@@ -0,0 +1,53 @@
+title = Yii::t('db_rbac', 'Редактирование правила: ') . ' ' . $permit->description;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('db_rbac', 'Правила доступа'), 'url' => ['permission']];
+$this->params['breadcrumbs'][] = Yii::t('db_rbac', 'Редактирование правила');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+
\ No newline at end of file
diff --git a/common/components/developeruz/db_rbac/views/access/updateRole.php b/common/components/developeruz/db_rbac/views/access/updateRole.php
new file mode 100644
index 0000000..e8bb002
--- /dev/null
+++ b/common/components/developeruz/db_rbac/views/access/updateRole.php
@@ -0,0 +1,54 @@
+title = Yii::t('db_rbac', 'Редактирование роли: ') . ' ' . $role->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('db_rbac', 'Управление ролями'), 'url' => ['role']];
+$this->params['breadcrumbs'][] = Yii::t('db_rbac', 'Редактирование');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+
diff --git a/common/components/developeruz/db_rbac/views/user/view.php b/common/components/developeruz/db_rbac/views/user/view.php
new file mode 100644
index 0000000..f88ebf2
--- /dev/null
+++ b/common/components/developeruz/db_rbac/views/user/view.php
@@ -0,0 +1,19 @@
+
+=Yii::t('db_rbac', 'Управление ролями пользователя');?> = $user->getUserName(); ?>
+ ["/{$moduleName}/user/update", 'id' => $user->getId()]]); ?>
+
+= Html::checkboxList('roles', $user_permit, $roles, ['separator' => ' ']); ?>
+
+
+ = Html::submitButton(Yii::t('db_rbac', 'Сохранить'), ['class' => 'btn btn-success']) ?>
+
+
+
+
diff --git a/common/components/nodge/eauth/.gitignore b/common/components/nodge/eauth/.gitignore
new file mode 100644
index 0000000..2c10524
--- /dev/null
+++ b/common/components/nodge/eauth/.gitignore
@@ -0,0 +1,3 @@
+vendor
+composer.lock
+composer.phar
\ No newline at end of file
diff --git a/common/components/nodge/eauth/CHANGELOG.md b/common/components/nodge/eauth/CHANGELOG.md
new file mode 100644
index 0000000..4ec7f9b
--- /dev/null
+++ b/common/components/nodge/eauth/CHANGELOG.md
@@ -0,0 +1,107 @@
+Yii2 EAuth Change Log
+=====================
+
+### 2.3.0 (17.10.2015)
+* Added InstagramOAuth2Service (#61)
+* Fixed default token lifetime (#53)
+* Replace array() with [] (#54)
+* Remove deprecated Google OpenID service (#56)
+* Remove deprecated Yandex OpenID service
+
+### 2.2.4 (27.07.2015)
+* Fixed typo in `oauth2/Service.php` (#34)
+* Added German translation
+* Added `email` attribute to `LinkedinOAuth2Service.php`
+
+### 2.2.3 (15.07.2014)
+* Added ability to call public api methods (without access token) (#28)
+
+### 2.2.2 (15.07.2014)
+* Fixed wrong redirect_uri when popup is used
+
+### 2.2.1 (25.04.2014)
+* Fix missing query params in callback urls (#26)
+* Follow Yii2 code style
+
+### 2.2.0 (19.04.2014)
+* Support for PHPoAuthLib v0.3 (#22)
+* Support for Yii2 beta
+* Internal state implementation replaced to PHPoAuthLib storage
+
+### 2.1.5 (24.03.2014)
+* Fixed Yii2 refactor (#17)
+* PSR-4
+
+### 2.1.4 (11.03.2014)
+* Fixed wrong callbackUrl in oauth\ServiceBase when UrlManager uses prettyUrl=false and showScript=false (#12)
+* Fixed Yii::t() calls according to Yii2 i18n Named Placeholders (#14)
+* Fixed Yii2 refactor #2630 (#15)
+
+### 2.1.3 (30.01.2014)
+* Yii2 update (Request methods has been refactored).
+
+### 2.1.2 (17.01.2014)
+* Fixed typo in oauth2\ServiceProxy
+
+### 2.1.1 (07.01.2014)
+* Fixed scope validation for OAuth services.
+
+### 2.1.0 (22.12.2013)
+* Reorganize project with new namespace.
+* Assets bundle has been moved.
+* Fixed typo in HttpClient (#8).
+* Added default User-Agent header to HttpClient.
+* Disabled CSRF validation for OpenID callbacks.
+* Optimized icons file.
+* Added SteamOpenIDService.
+* Improved redirect widget.
+
+### 2.0.3 (26.10.2013)
+* Fixed redirect_uri when not using url rule (#2).
+* Fixed hasValidAccessToken() method for OAuth1 services (#3).
+* Fixed auto login cookie (#4).
+
+### 2.0.2 (12.10.2013)
+* Fixed ServiceProxy constructor to match its interface (#1).
+* Added HttpClient with logging support and curl/streams fallback.
+* TokenStorage and HttpClient are configurable now.
+
+### 2.0.1 (08.09.2013)
+* Fixed package versions in the composer.json.
+* Fixed directories names.
+* Added support for custom scope separator in OAuth2 services.
+* Added support for additional headers for OAuth2 requests.
+* Added method to get error from access token response.
+* Added GitHubOAuth2Service.
+* Added LinkedinOAuth2Service.
+* Added MailruOAuth2Service.
+* Added OdnoklassnikiOAuth2Service.
+* Added LiveOAuth2Service.
+* Added YahooOpenIDService.
+
+### Version 2.0.0 (03.09.2013)
+* Use curl for http requests by default.
+* getIsAuthenticated() function now looks up for existing access token for all OAuth services.
+* Added support for oauth_expires_in to OAuth1 services.
+* Added error handlers to OAuth1 services.
+* Added support for refresh tokens to OAuth2 ServiceProxy.
+* Added an option to disable OAuth2 state validation.
+
+### 31.08.2013
+* Reorganize directories. Separate root directory by service type.
+* Fixed OAuthService::getCallbackUrl(). Now returns url without GET arguments.
+* Fixed typos in OAuth services.
+* Fixed OpenID loadAttributes functions.
+* OAuth2 display mode handling moved to the base class.
+* Added OAuthService::getAccessTokenData() method to access to valid access_token and related data.
+* Added token default lifetime setting.
+* Added "state" argument handling for OAuth2 services to improve security.
+* Updated OpenID library. Fixed error with stream requests.
+* Added VKontakteOAuth2Service.
+* Added GoogleOAuth2Service.
+* Added GoogleOAuth2Service.
+* Added YandexOAuth2Service.
+* Added session token storage using Yii session.
+
+### 30.08.2013
+* Initial release for Yii2.
diff --git a/common/components/nodge/eauth/LICENSE b/common/components/nodge/eauth/LICENSE
new file mode 100644
index 0000000..9992a7c
--- /dev/null
+++ b/common/components/nodge/eauth/LICENSE
@@ -0,0 +1,8 @@
+Copyright (c) 2011, Maxim Zemskov
+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.
+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 HOLDER 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.
\ No newline at end of file
diff --git a/common/components/nodge/eauth/README.md b/common/components/nodge/eauth/README.md
new file mode 100644
index 0000000..704bb5c
--- /dev/null
+++ b/common/components/nodge/eauth/README.md
@@ -0,0 +1,493 @@
+Yii2 EAuth extension
+====================
+
+EAuth extension allows to authenticate users with accounts on other websites.
+Supported protocols: OpenID, OAuth 1.0 and OAuth 2.0.
+
+EAuth is an extension to provide a unified (does not depend on the selected service) method to authenticate the user. The extension itself does not perform login, does not register the user and does not bind the user accounts from different providers.
+
+* [Demo](http://nodge.ru/yii-eauth/demo2/)
+* [Demo project](https://github.com/Nodge/yii2-eauth-demo/)
+* [Installation](#installation)
+* [Version for yii 1.1](https://github.com/Nodge/yii-eauth/)
+
+### Why own extension and not a third-party service?
+The implementation of the authorization on your own server has several advantages:
+
+* Full control over the process: What will be written in the authorization window, what data we get, etc.
+* Ability to change the appearance of the widget.
+* When logging in via OAuth, it is possible to invoke methods on the API.
+* Fewer dependencies on third-party services - more reliable application.
+
+
+### The extension allows you to:
+
+* Ignore the nuances of authorization through the different types of services and use the class based adapters for each service.
+* Get a unique user ID that can be used to register the user in your application.
+* Extend the standard authorization classes to obtain additional data about the user.
+* Work with the API of social networks by extending the authorization classes.
+* Set up a list of supported services, customize the appearance of the widget, use the popup window without closing your application.
+
+
+### Extension includes:
+
+* The component that contains utility functions.
+* A widget that displays a list of services in the form of icons and allowing authorization in the popup window.
+* Base classes to create your own services.
+* Ready to authenticate via Google, Twitter, Facebook and other providers.
+
+
+### Included services:
+
+* OpenID:
+ * Yahoo
+ * Steam
+* OAuth1:
+ * Twitter
+ * LinkedIn
+* OAuth2:
+ * Google
+ * Facebook
+ * Live
+ * GitHub
+ * LinkedIn
+ * Instagram
+ * Yandex (ru)
+ * VKontake (ru)
+ * Mail.ru (ru)
+ * Odnoklassniki (ru)
+
+
+### Resources
+
+* [Yii EAuth](https://github.com/Nodge/yii2-eauth)
+* [Demo](http://nodge.ru/yii-eauth/demo2/)
+* [Demo project](https://github.com/Nodge/yii2-eauth-demo/)
+* [Yii Framework](http://yiiframework.com/)
+* [OpenID](http://openid.net/)
+* [OAuth](http://oauth.net/)
+* [OAuth 2.0](http://oauth.net/2/)
+* [LightOpenID](https://github.com/iignatov/LightOpenID)
+* [PHPoAuthLib](https://github.com/Lusitanian/PHPoAuthLib)
+
+
+### Requirements
+
+* Yii 2.0 or above
+* curl php extension
+* LightOpenId
+* PHPoAuthLib
+
+
+# Installation
+
+This library can be found on [Packagist](https://packagist.org/packages/nodge/yii2-eauth).
+The recommended way to install this is through [composer](http://getcomposer.org).
+
+Edit your `composer.json` and add:
+
+```json
+{
+ "require": {
+ "nodge/yii2-eauth": "~2.0"
+ }
+}
+```
+
+And install dependencies:
+
+```bash
+$ curl -sS https://getcomposer.org/installer | php
+$ php composer.phar install
+```
+
+
+# Usage
+
+## Demo project
+
+The source code of the [demo](http://nodge.ru/yii-eauth/demo2/) is available [here](https://github.com/Nodge/yii2-eauth-demo/).
+
+
+## Basic setup
+
+### Configuration
+
+Add the following in your config:
+
+```php
+ [
+ 'eauth' => [
+ 'class' => 'nodge\eauth\EAuth',
+ 'popup' => true, // Use the popup window instead of redirecting.
+ 'cache' => false, // Cache component name or false to disable cache. Defaults to 'cache' on production environments.
+ 'cacheExpire' => 0, // Cache lifetime. Defaults to 0 - means unlimited.
+ 'httpClient' => [
+ // uncomment this to use streams in safe_mode
+ //'useStreamsFallback' => true,
+ ],
+ 'services' => [ // You can change the providers and their classes.
+ 'google' => [
+ // register your app here: https://code.google.com/apis/console/
+ 'class' => 'nodge\eauth\services\GoogleOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ 'title' => 'Google',
+ ],
+ 'twitter' => [
+ // register your app here: https://dev.twitter.com/apps/new
+ 'class' => 'nodge\eauth\services\TwitterOAuth1Service',
+ 'key' => '...',
+ 'secret' => '...',
+ ],
+ 'yandex' => [
+ // register your app here: https://oauth.yandex.ru/client/my
+ 'class' => 'nodge\eauth\services\YandexOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ 'title' => 'Yandex',
+ ],
+ 'facebook' => [
+ // register your app here: https://developers.facebook.com/apps/
+ 'class' => 'nodge\eauth\services\FacebookOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ ],
+ 'yahoo' => [
+ 'class' => 'nodge\eauth\services\YahooOpenIDService',
+ //'realm' => '*.example.org', // your domain, can be with wildcard to authenticate on subdomains.
+ ],
+ 'linkedin' => [
+ // register your app here: https://www.linkedin.com/secure/developer
+ 'class' => 'nodge\eauth\services\LinkedinOAuth1Service',
+ 'key' => '...',
+ 'secret' => '...',
+ 'title' => 'LinkedIn (OAuth1)',
+ ],
+ 'linkedin_oauth2' => [
+ // register your app here: https://www.linkedin.com/secure/developer
+ 'class' => 'nodge\eauth\services\LinkedinOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ 'title' => 'LinkedIn (OAuth2)',
+ ],
+ 'github' => [
+ // register your app here: https://github.com/settings/applications
+ 'class' => 'nodge\eauth\services\GitHubOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ ],
+ 'live' => [
+ // register your app here: https://account.live.com/developers/applications/index
+ 'class' => 'nodge\eauth\services\LiveOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ ],
+ 'steam' => [
+ 'class' => 'nodge\eauth\services\SteamOpenIDService',
+ //'realm' => '*.example.org', // your domain, can be with wildcard to authenticate on subdomains.
+ ],
+ 'instagram' => [
+ // register your app here: https://instagram.com/developer/register/
+ 'class' => 'nodge\eauth\services\InstagramOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ ],
+ 'vkontakte' => [
+ // register your app here: https://vk.com/editapp?act=create&site=1
+ 'class' => 'nodge\eauth\services\VKontakteOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ ],
+ 'mailru' => [
+ // register your app here: http://api.mail.ru/sites/my/add
+ 'class' => 'nodge\eauth\services\MailruOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ ],
+ 'odnoklassniki' => [
+ // register your app here: http://dev.odnoklassniki.ru/wiki/pages/viewpage.action?pageId=13992188
+ // ... or here: http://www.odnoklassniki.ru/dk?st.cmd=appsInfoMyDevList&st._aid=Apps_Info_MyDev
+ 'class' => 'nodge\eauth\services\OdnoklassnikiOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ 'clientPublic' => '...',
+ 'title' => 'Odnoklas.',
+ ],
+ ],
+ ],
+
+ 'i18n' => [
+ 'translations' => [
+ 'eauth' => [
+ 'class' => 'yii\i18n\PhpMessageSource',
+ 'basePath' => '@eauth/messages',
+ ],
+ ],
+ ],
+
+ // (optionally) you can configure pretty urls
+ 'urlManager' => [
+ 'enablePrettyUrl' => true,
+ 'showScriptName' => false,
+ 'rules' => [
+ 'login/' => 'site/login',
+ ],
+ ],
+
+ // (optionally) you can configure logging
+ 'log' => [
+ 'targets' => [
+ [
+ 'class' => 'yii\log\FileTarget',
+ 'logFile' => '@app/runtime/logs/eauth.log',
+ 'categories' => ['nodge\eauth\*'],
+ 'logVars' => [],
+ ],
+ ],
+ ],
+ ...
+ ],
+...
+```
+
+### User model
+
+You need to modify your User model to login with EAuth services.
+Example from demo project:
+
+```php
+getSession()->has('user-'.$id)) {
+ return new self(Yii::$app->getSession()->get('user-'.$id));
+ }
+ else {
+ return isset(self::$users[$id]) ? new self(self::$users[$id]) : null;
+ }
+ }
+
+ /**
+ * @param \nodge\eauth\ServiceBase $service
+ * @return User
+ * @throws ErrorException
+ */
+ public static function findByEAuth($service) {
+ if (!$service->getIsAuthenticated()) {
+ throw new ErrorException('EAuth user should be authenticated before creating identity.');
+ }
+
+ $id = $service->getServiceName().'-'.$service->getId();
+ $attributes = [
+ 'id' => $id,
+ 'username' => $service->getAttribute('name'),
+ 'authKey' => md5($id),
+ 'profile' => $service->getAttributes(),
+ ];
+ $attributes['profile']['service'] = $service->getServiceName();
+ Yii::$app->getSession()->set('user-'.$id, $attributes);
+ return new self($attributes);
+ }
+...
+```
+
+Then you can access to EAuth attributes through:
+
+```php
+getUser()->getIdentity();
+ if (isset($identity->profile)) {
+ VarDumper::dump($identity->profile, 10, true);
+ }
+```
+
+### Controller
+
+Attach OpenID Controller behavior to disable CSRF validation for OpenID callbacks.
+Or you can disable CSRF validation by yourself.
+
+```php
+ [
+ // required to disable csrf validation on OpenID requests
+ 'class' => \nodge\eauth\openid\ControllerBehavior::className(),
+ 'only' => ['login'],
+ ],
+ ];
+ }
+...
+```
+
+Add the following to your Login action:
+
+```php
+getRequest()->getQueryParam('service');
+ if (isset($serviceName)) {
+ /** @var $eauth \nodge\eauth\ServiceBase */
+ $eauth = Yii::$app->get('eauth')->getIdentity($serviceName);
+ $eauth->setRedirectUrl(Yii::$app->getUser()->getReturnUrl());
+ $eauth->setCancelUrl(Yii::$app->getUrlManager()->createAbsoluteUrl('site/login'));
+
+ try {
+ if ($eauth->authenticate()) {
+// var_dump($eauth->getIsAuthenticated(), $eauth->getAttributes()); exit;
+
+ $identity = User::findByEAuth($eauth);
+ Yii::$app->getUser()->login($identity);
+
+ // special redirect with closing popup window
+ $eauth->redirect();
+ }
+ else {
+ // close popup window and redirect to cancelUrl
+ $eauth->cancel();
+ }
+ }
+ catch (\nodge\eauth\ErrorException $e) {
+ // save error to show it later
+ Yii::$app->getSession()->setFlash('error', 'EAuthException: '.$e->getMessage());
+
+ // close popup window and redirect to cancelUrl
+// $eauth->cancel();
+ $eauth->redirect($eauth->getCancelUrl());
+ }
+ }
+
+ // default authorization code through login/password ..
+ }
+...
+```
+
+### View
+
+```php
+...
+getSession()->hasFlash('error')) {
+ echo ''.Yii::$app->getSession()->getFlash('error').'
';
+ }
+?>
+...
+Do you already have an account on one of these sites? Click the logo to log in with it here:
+ 'site/login']); ?>
+...
+```
+
+
+## Extending
+
+To receive all the necessary data to your application, you can override the base class of any provider.
+Base classes are stored in `@eauth/src/services`.
+Examples of extended classes can be found in `@eauth/src/services/extended/`.
+
+After overriding the base class, you need to update your configuration file with a new class name.
+
+
+## Working with OAuth API
+
+You can extend base classes with necessary methods and then write something like this:
+
+```php
+eauth->getIdentity('facebook');
+
+ // to get protected resources user should be authenticated:
+ if ($eauth->getIsAuthenticated()) {
+ $eauth->callProtectedApiMethod();
+ $eauth->callAnotherProtectedApiMethod();
+ }
+
+ // or you can get public resources at any time:
+ $eauth->callPublicApiMethod();
+ $eauth->callAnotherPublicApiMethod();
+```
+
+Example of an API call method:
+
+```php
+makeSignedRequest($api_method, [
+ 'query' => [ 'foo' => 'bar' ], // GET arguments
+ 'data' => [ 'foo' => 'bar' ], // POST arguments
+ 'headers' => [ 'X-Foo' => 'bar' ], // Extra HTTP headers
+ ]);
+
+ // you can get public resources with the same API:
+ //$response = $this->makeRequest($api_method, $options);
+
+ // process $response
+ $data = process($response);
+
+ // return results
+ return $data;
+ }
+ }
+```
+
+API calls are performed if the current user has a valid access token (saved during the authentication).
+You can save access_token to your database by using custom token storage in your config:
+
+```php
+ [
+ 'eauth' => [
+ 'class' => 'nodge\eauth\EAuth',
+ 'tokenStorage' => [
+ 'class' => '@app\eauth\DatabaseTokenStorage',
+ ],
+ ],
+ ...
+ ],
+...
+```
+
+
+## Translation
+
+To use translations, add the following in your config:
+
+```php
+ [
+ 'i18n' => [
+ 'translations' => [
+ 'eauth' => [
+ 'class' => 'yii\i18n\PhpMessageSource',
+ 'basePath' => '@eauth/messages',
+ ],
+ ],
+ ],
+ ...
+ ],
+...
+```
+
+Available translations can be found in `@eauth/src/messages`.
+
+
+# License
+
+The extension was released under the [New BSD License](http://www.opensource.org/licenses/bsd-license.php), so you'll find the latest version on [GitHub](https://github.com/Nodge/yii2-eauth).
diff --git a/common/components/nodge/eauth/composer.json b/common/components/nodge/eauth/composer.json
new file mode 100644
index 0000000..2bc2677
--- /dev/null
+++ b/common/components/nodge/eauth/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "nodge/yii2-eauth",
+ "description": "Yii2 EAuth Extension. EAuth allows to authenticate users with accounts on other websites (Google, Facebook, Twitter, etc).",
+ "keywords": ["yii2", "extension", "eauth", "openid", "oauth", "authentication"],
+ "homepage": "https://github.com/Nodge/yii2-eauth",
+ "type": "yii2-extension",
+ "license": "New BSD License",
+ "authors": [
+ {
+ "name": "Maxim Zemskov",
+ "email": "nodge@yandex.ru",
+ "homepage": "http://nodge.ru/"
+ }
+ ],
+ "support": {
+ "source": "https://github.com/nodge/yii2-eauth"
+ },
+ "autoload": {
+ "psr-4": {
+ "common\modules\nodge\\eauth\\": "src/"
+ }
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "lib-curl": "*",
+ "yiisoft/yii2": "*",
+ "lusitanian/oauth": "~0.3.0",
+ "nodge/lightopenid": "~1.1.0"
+ },
+ "extra": {
+ "bootstrap": "common\modules\nodge\\eauth\\Bootstrap"
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/Bootstrap.php b/common/components/nodge/eauth/src/Bootstrap.php
new file mode 100644
index 0000000..1312c88
--- /dev/null
+++ b/common/components/nodge/eauth/src/Bootstrap.php
@@ -0,0 +1,28 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\modules\nodge\eauth\src\eauth;
+
+use Yii;
+use yii\base\Application;
+use yii\base\BootstrapInterface;
+
+/**
+ * This is the bootstrap class for the yii2-eauth extension.
+ */
+class Bootstrap implements BootstrapInterface
+{
+ /**
+ * @inheritdoc
+ */
+ public function bootstrap($app)
+ {
+ Yii::setAlias('@eauth', __DIR__);
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/EAuth.php b/common/components/nodge/eauth/src/EAuth.php
new file mode 100644
index 0000000..11b0970
--- /dev/null
+++ b/common/components/nodge/eauth/src/EAuth.php
@@ -0,0 +1,316 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src;
+
+use Yii;
+use yii\base\Object;
+use yii\helpers\ArrayHelper;
+use yii\helpers\Url;
+
+/**
+ * The EAuth class provides simple authentication via OpenID and OAuth providers.
+ *
+ * @package application.extensions.eauth
+ */
+class EAuth extends Object
+{
+
+ /**
+ * @var array Authorization services and their settings.
+ */
+ protected $services = [];
+
+ /**
+ * @var boolean Whether to use popup window for the authorization dialog.
+ */
+ protected $popup = true;
+
+ /**
+ * @var string|bool Cache component name to use. False to disable cache.
+ */
+ public $cache = null;
+
+ /**
+ * @var integer the number of seconds in which the cached value will expire. 0 means never expire.
+ */
+ public $cacheExpire = 0;
+
+ /**
+ * @var string popup redirect view with custom js code
+ */
+ protected $redirectWidget = '\\nodge\\eauth\\RedirectWidget';
+
+ /**
+ * @var array TokenStorage class.
+ */
+ protected $tokenStorage = [
+ 'class' => 'nodge\eauth\oauth\SessionTokenStorage',
+ ];
+
+ /**
+ * @var array HttpClient class.
+ */
+ protected $httpClient = [
+ 'class' => 'nodge\eauth\oauth\HttpClient',
+// 'useStreamsFallback' => false,
+ ];
+
+ /**
+ * Initialize the component.
+ */
+ public function init()
+ {
+ parent::init();
+
+ // set default cache on production environments
+ if (!isset($this->cache) && YII_ENV_PROD) {
+ $this->cache = 'cache';
+ }
+ }
+
+ /**
+ * @param array $services
+ */
+ public function setServices($services)
+ {
+ $this->services = $services;
+ }
+
+ /**
+ * Returns services settings declared in the authorization classes.
+ * For perfomance reasons it uses cache to store settings array.
+ *
+ * @return \stdClass[] services settings.
+ */
+ public function getServices()
+ {
+ $services = false;
+ if (!empty($this->cache) && Yii::$app->has($this->cache)) {
+ /** @var $cache \yii\caching\Cache */
+ $cache = Yii::$app->get($this->cache);
+ $services = $cache->get('EAuth.services');
+ }
+
+ if (false === $services || !is_array($services)) {
+ $services = [];
+ foreach ($this->services as $service => $options) {
+ /** @var $class ServiceBase */
+ $class = $this->getIdentity($service);
+ $services[$service] = (object)[
+ 'id' => $class->getServiceName(),
+ 'title' => $class->getServiceTitle(),
+ 'type' => $class->getServiceType(),
+ 'jsArguments' => $class->getJsArguments(),
+ ];
+ }
+ if (isset($cache)) {
+ $cache->set('EAuth.services', $services, $this->cacheExpire);
+ }
+ }
+ return $services;
+ }
+
+ /**
+ * @param bool $usePopup
+ */
+ public function setPopup($usePopup)
+ {
+ $this->popup = $usePopup;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getPopup()
+ {
+ return $this->popup;
+ }
+
+ /**
+ * @param string|bool $cache
+ */
+ public function setCache($cache)
+ {
+ $this->cache = $cache;
+ }
+
+ /**
+ * @return string|bool
+ */
+ public function getCache()
+ {
+ return $this->cache;
+ }
+
+ /**
+ * @param int $cacheExpire
+ */
+ public function setCacheExpire($cacheExpire)
+ {
+ $this->cacheExpire = $cacheExpire;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCacheExpire()
+ {
+ return $this->cacheExpire;
+ }
+
+ /**
+ * @param string $redirectWidget
+ */
+ public function setRedirectWidget($redirectWidget)
+ {
+ $this->redirectWidget = $redirectWidget;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRedirectWidget()
+ {
+ return $this->redirectWidget;
+ }
+
+ /**
+ * @param array $config
+ */
+ public function setTokenStorage(array $config)
+ {
+ $this->tokenStorage = ArrayHelper::merge($this->tokenStorage, $config);
+ }
+
+ /**
+ * @return array
+ */
+ public function getTokenStorage()
+ {
+ return $this->tokenStorage;
+ }
+
+ /**
+ * @param array $config
+ */
+ public function setHttpClient(array $config)
+ {
+ $this->httpClient = ArrayHelper::merge($this->httpClient, $config);
+ }
+
+ /**
+ * @return array
+ */
+ public function getHttpClient()
+ {
+ return $this->httpClient;
+ }
+
+ /**
+ * Returns the settings of the service.
+ *
+ * @param string $service the service name.
+ * @return \stdClass the service settings.
+ * @throws ErrorException
+ */
+ protected function getService($service)
+ {
+ $service = strtolower($service);
+ $services = $this->getServices();
+ if (!isset($services[$service])) {
+ throw new ErrorException(Yii::t('eauth', 'Undefined service name: {service}.', ['service' => $service]), 500);
+ }
+ return $services[$service];
+ }
+
+ /**
+ * Returns the type of the service.
+ *
+ * @param string $service the service name.
+ * @return string the service type.
+ */
+ public function getServiceType($service)
+ {
+ $service = $this->getService($service);
+ return $service->type;
+ }
+
+ /**
+ * Returns the service identity class.
+ *
+ * @param string $service the service name.
+ * @return IAuthService the identity class.
+ * @throws ErrorException
+ */
+ public function getIdentity($service)
+ {
+ $service = strtolower($service);
+ if (!isset($this->services[$service])) {
+ throw new ErrorException(Yii::t('eauth', 'Undefined service name: {service}.', ['service' => $service]), 500);
+ }
+ $service = $this->services[$service];
+
+ $service['component'] = $this;
+
+ /** @var $identity IAuthService */
+ $identity = Yii::createObject($service);
+ return $identity;
+ }
+
+ /**
+ * Redirects to url. If the authorization dialog opened in the popup window,
+ * it will be closed instead of redirect. Set $jsRedirect=true if you want
+ * to redirect anyway.
+ *
+ * @param mixed $url url to redirect. Can be route or normal url. See {@link CHtml::normalizeUrl}.
+ * @param boolean $jsRedirect whether to use redirect while popup window is used. Defaults to true.
+ * @param array $params
+ */
+ public function redirect($url, $jsRedirect = true, $params = [])
+ {
+ /** @var RedirectWidget $widget */
+ $widget = Yii::createObject([
+ 'class' => $this->redirectWidget,
+ 'url' => Url::to($url),
+ 'redirect' => $jsRedirect,
+ 'params' => $params
+ ]);
+ ob_start();
+ $widget->run();
+ $output = ob_get_clean();
+ $response = Yii::$app->getResponse();
+ $response->content = $output;
+ $response->send();
+ exit();
+ }
+
+ /**
+ * Serialize the identity class.
+ *
+ * @param ServiceBase $identity the class instance.
+ * @return string serialized value.
+ */
+ public function toString($identity)
+ {
+ return serialize($identity);
+ }
+
+ /**
+ * Serialize the identity class.
+ *
+ * @param string $identity serialized value.
+ * @return ServiceBase the class instance.
+ */
+ public function fromString($identity)
+ {
+ return unserialize($identity);
+ }
+
+}
diff --git a/common/components/nodge/eauth/src/ErrorException.php b/common/components/nodge/eauth/src/ErrorException.php
new file mode 100644
index 0000000..c23cf7d
--- /dev/null
+++ b/common/components/nodge/eauth/src/ErrorException.php
@@ -0,0 +1,15 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\modules\nodge\eauth\src\eauth;
+
+class ErrorException extends \yii\base\ErrorException
+{
+
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/IAuthService.php b/common/components/nodge/eauth/src/IAuthService.php
new file mode 100644
index 0000000..36fe921
--- /dev/null
+++ b/common/components/nodge/eauth/src/IAuthService.php
@@ -0,0 +1,129 @@
+
+ * @link http://github.com/Nodge/yii-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\modules\nodge\eauth\src\eauth;
+
+/**
+ * IAuthService is the interface for all service types and providers.
+ *
+ * @package application.extensions.eauth
+ */
+interface IAuthService
+{
+
+ /**
+ * Returns service name(id).
+ */
+ public function getServiceName();
+
+ /**
+ * Returns service title.
+ */
+ public function getServiceTitle();
+
+ /**
+ * Returns service type (e.g. OpenID, OAuth).
+ */
+ public function getServiceType();
+
+ /**
+ * Returns arguments for the jQuery.eauth() javascript function.
+ */
+ public function getJsArguments();
+
+
+ /**
+ * Sets {@link EAuth} application component
+ *
+ * @param EAuth $component the application auth component.
+ */
+ public function setComponent($component);
+
+ /**
+ * Returns the {@link EAuth} application component.
+ */
+ public function getComponent();
+
+
+ /**
+ * Sets redirect url after successful authorization.
+ *
+ * @param string $url url to redirect.
+ */
+ public function setRedirectUrl($url);
+
+ /**
+ * Returns the redirect url after successful authorization.
+ */
+ public function getRedirectUrl();
+
+
+ /**
+ * Sets redirect url after unsuccessful authorization (e.g. user canceled).
+ *
+ * @param string $url url to redirect.
+ */
+ public function setCancelUrl($url);
+
+ /**
+ * Returns the redirect url after unsuccessful authorization (e.g. user canceled).
+ */
+ public function getCancelUrl();
+
+
+ /**
+ * Authenticate the user.
+ */
+ public function authenticate();
+
+ /**
+ * Whether user was successfuly authenticated.
+ */
+ public function getIsAuthenticated();
+
+
+ /**
+ * Redirect to the url. If url is null, {@link redirectUrl} will be used.
+ *
+ * @param string $url url to redirect.
+ */
+ public function redirect($url = null);
+
+ /**
+ * Redirect to the {@link cancelUrl} or simply close the popup window.
+ */
+ public function cancel();
+
+
+ /**
+ * Returns the user unique id.
+ */
+ public function getId();
+
+ /**
+ * Returns the array that contains all available authorization attributes.
+ */
+ public function getAttributes();
+
+ /**
+ * Returns the authorization attribute value.
+ *
+ * @param string $key the attribute name.
+ * @param mixed $default the default value.
+ */
+ public function getAttribute($key, $default = null);
+
+ /**
+ * Whether the authorization attribute exists.
+ *
+ * @param string $key the attribute name.
+ */
+ public function hasAttribute($key);
+
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/RedirectWidget.php b/common/components/nodge/eauth/src/RedirectWidget.php
new file mode 100644
index 0000000..aeb74e9
--- /dev/null
+++ b/common/components/nodge/eauth/src/RedirectWidget.php
@@ -0,0 +1,56 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\modules\nodge\eauth\src\eauth;
+
+use \Yii;
+use yii\helpers\ArrayHelper;
+
+/**
+ * The EAuthRedirectWidget widget displays the redirect page after returning from provider.
+ *
+ * @package application.extensions.eauth
+ */
+class RedirectWidget extends Widget
+{
+
+ /**
+ * @var mixed the widget mode. Default to "login".
+ */
+ public $url = null;
+
+ /**
+ * @var boolean whether to use redirect inside the popup window.
+ */
+ public $redirect = true;
+
+ /**
+ * @var string
+ */
+ public $view = 'redirect';
+
+ /**
+ * @var array
+ */
+ public $params = [];
+
+ /**
+ * Executes the widget.
+ */
+ public function run()
+ {
+ echo $this->render($this->view,
+ ArrayHelper::merge([
+ 'id' => $this->getId(),
+ 'url' => $this->url,
+ 'redirect' => $this->redirect,
+ ], $this->params)
+ );
+ }
+}
diff --git a/common/components/nodge/eauth/src/ServiceBase.php b/common/components/nodge/eauth/src/ServiceBase.php
new file mode 100644
index 0000000..689679d
--- /dev/null
+++ b/common/components/nodge/eauth/src/ServiceBase.php
@@ -0,0 +1,359 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\modules\nodge\eauth\src\eauth;
+
+use Yii;
+use yii\base\Object;
+use yii\helpers\Url;
+
+/**
+ * EAuthServiceBase is a base class for providers.
+ *
+ * @package application.extensions.eauth
+ */
+abstract class ServiceBase extends Object implements IAuthService
+{
+
+ /**
+ * @var string the service name.
+ */
+ protected $name;
+
+ /**
+ *
+ * @var string the service title to display in views.
+ */
+ protected $title;
+
+ /**
+ * @var string the service type (e.g. OpenID, OAuth).
+ */
+ protected $type;
+
+ /**
+ * @var array arguments for the jQuery.eauth() javascript function.
+ */
+ protected $jsArguments = [];
+
+ /**
+ * @var array authorization attributes.
+ * @see getAttribute
+ * @see getItem
+ */
+ protected $attributes = [];
+
+ /**
+ * @var boolean whether user was successfuly authenticated.
+ * @see getIsAuthenticated
+ */
+ protected $authenticated = false;
+
+ /**
+ * @var boolean whether is attributes was fetched.
+ */
+ private $fetched = false;
+
+ /**
+ * @var EAuth the {@link EAuth} application component.
+ */
+ private $component;
+
+ /**
+ * @var string the redirect url after successful authorization.
+ */
+ private $redirectUrl = '';
+
+ /**
+ * @var string the redirect url after unsuccessful authorization (e.g. user canceled).
+ */
+ private $cancelUrl = '';
+
+ /**
+ * PHP getter magic method.
+ * This method is overridden so that service attributes can be accessed like properties.
+ *
+ * @param string $name property name.
+ * @return mixed property value.
+ * @see getAttribute
+ */
+ public function __get($name)
+ {
+ if ($this->hasAttribute($name)) {
+ return $this->getAttribute($name);
+ } else {
+ return parent::__get($name);
+ }
+ }
+
+ /**
+ * Checks if a attribute value is null.
+ * This method overrides the parent implementation by checking
+ * if the attribute is null or not.
+ *
+ * @param string $name the attribute name.
+ * @return boolean whether the attribute value is null.
+ */
+ public function __isset($name)
+ {
+ if ($this->hasAttribute($name)) {
+ return true;
+ } else {
+ return parent::__isset($name);
+ }
+ }
+
+ /**
+ * Initialize the component.
+ * Sets the default {@link redirectUrl} and {@link cancelUrl}.
+ */
+ public function init()
+ {
+ parent::init();
+
+ $this->setRedirectUrl(Yii::$app->getUser()->getReturnUrl());
+
+ $service = Yii::$app->getRequest()->getQueryParam('service');
+ $cancelUrl = Url::to(['', 'service' => $service], true);
+
+ $this->setCancelUrl($cancelUrl);
+ }
+
+ /**
+ * Returns service name(id).
+ *
+ * @return string the service name(id).
+ */
+ public function getServiceName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Returns service title.
+ *
+ * @return string the service title.
+ */
+ public function getServiceTitle()
+ {
+ return Yii::t('eauth', $this->title);
+ }
+
+ /**
+ * Returns service type (e.g. OpenID, OAuth).
+ *
+ * @return string the service type (e.g. OpenID, OAuth).
+ */
+ public function getServiceType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Returns arguments for the jQuery.eauth() javascript function.
+ *
+ * @return array the arguments for the jQuery.eauth() javascript function.
+ */
+ public function getJsArguments()
+ {
+ return $this->jsArguments;
+ }
+
+ /**
+ * Sets {@link EAuth} application component
+ *
+ * @param EAuth $component the application auth component.
+ */
+ public function setComponent($component)
+ {
+ $this->component = $component;
+ }
+
+ /**
+ * Returns the {@link EAuth} application component.
+ *
+ * @return EAuth the {@link EAuth} application component.
+ */
+ public function getComponent()
+ {
+ return $this->component;
+ }
+
+ /**
+ * Sets redirect url after successful authorization.
+ *
+ * @param string $url to redirect.
+ */
+ public function setRedirectUrl($url)
+ {
+ $this->redirectUrl = $url;
+ }
+
+ /**
+ * @return string the redirect url after successful authorization.
+ */
+ public function getRedirectUrl()
+ {
+ return $this->redirectUrl;
+ }
+
+ /**
+ * Sets redirect url after unsuccessful authorization (e.g. user canceled).
+ *
+ * @param string $url
+ */
+ public function setCancelUrl($url)
+ {
+ $this->cancelUrl = $url;
+ }
+
+ /**
+ * @return string the redirect url after unsuccessful authorization (e.g. user canceled).
+ */
+ public function getCancelUrl()
+ {
+ return $this->cancelUrl;
+ }
+
+ /**
+ * @param string $title
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * Authenticate the user.
+ *
+ * @return boolean whether user was successfuly authenticated.
+ */
+ public function authenticate()
+ {
+ return $this->getIsAuthenticated();
+ }
+
+ /**
+ * Whether user was successfuly authenticated.
+ *
+ * @return boolean whether user was successfuly authenticated.
+ */
+ public function getIsAuthenticated()
+ {
+ return $this->authenticated;
+ }
+
+ /**
+ * Redirect to the url. If url is null, {@link redirectUrl} will be used.
+ *
+ * @param string $url url to redirect.
+ * @param array $params
+ */
+ public function redirect($url = null, $params = [])
+ {
+ $this->component->redirect(isset($url) ? $url : $this->redirectUrl, true, $params);
+ }
+
+ /**
+ * Redirect to the {@link cancelUrl} or simply close the popup window.
+ */
+ public function cancel($url = null)
+ {
+ $this->component->redirect(isset($url) ? $url : $this->cancelUrl, !$this->component->popup);
+ }
+
+ /**
+ * Fetch attributes array.
+ *
+ * @return boolean whether the attributes was successfully fetched.
+ */
+ protected function fetchAttributes()
+ {
+ return true;
+ }
+
+ /**
+ * Fetch attributes array.
+ * This function is internally used to handle fetched state.
+ */
+ protected function _fetchAttributes()
+ {
+ if (!$this->fetched) {
+ $this->fetched = true;
+ $result = $this->fetchAttributes();
+ if (isset($result)) {
+ $this->fetched = $result;
+ }
+ }
+ }
+
+ /**
+ * Returns the user unique id.
+ *
+ * @return mixed the user id.
+ */
+ public function getId()
+ {
+ $this->_fetchAttributes();
+ return $this->attributes['id'];
+ }
+
+ /**
+ * Returns the array that contains all available authorization attributes.
+ *
+ * @return array the attributes.
+ */
+ public function getAttributes()
+ {
+ $this->_fetchAttributes();
+ $attributes = [];
+ foreach ($this->attributes as $key => $val) {
+ $attributes[$key] = $this->getAttribute($key);
+ }
+ return $attributes;
+ }
+
+ /**
+ * Returns the authorization attribute value.
+ *
+ * @param string $key the attribute name.
+ * @param mixed $default the default value.
+ * @return mixed the attribute value.
+ */
+ public function getAttribute($key, $default = null)
+ {
+ $this->_fetchAttributes();
+ $getter = 'get' . $key;
+ if (method_exists($this, $getter)) {
+ return $this->$getter();
+ } else {
+ return isset($this->attributes[$key]) ? $this->attributes[$key] : $default;
+ }
+ }
+
+ /**
+ * Whether the authorization attribute exists.
+ *
+ * @param string $key the attribute name.
+ * @return boolean true if attribute exists, false otherwise.
+ */
+ public function hasAttribute($key)
+ {
+ $this->_fetchAttributes();
+ return isset($this->attributes[$key]);
+ }
+
+ /**
+ * @return bool
+ */
+ public function getIsInsidePopup()
+ {
+ return isset($_GET['js']);
+ }
+}
diff --git a/common/components/nodge/eauth/src/Widget.php b/common/components/nodge/eauth/src/Widget.php
new file mode 100644
index 0000000..2e30e18
--- /dev/null
+++ b/common/components/nodge/eauth/src/Widget.php
@@ -0,0 +1,108 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\modules\nodge\eauth\src\eauth;
+
+use Yii;
+
+/**
+ * The EAuthWidget widget prints buttons to authenticate user with OpenID and OAuth providers.
+ *
+ * @package application.extensions.eauth
+ */
+class Widget extends \yii\base\Widget
+{
+
+ /**
+ * @var string EAuth component name.
+ */
+ public $component = 'eauth';
+
+ /**
+ * @var array the services.
+ * @see EAuth::getServices()
+ */
+ public $services = null;
+
+ /**
+ * @var array predefined services. If null then use all services. Default is null.
+ */
+ public $predefinedServices = null;
+
+ /**
+ * @var boolean whether to use popup window for authorization dialog. Javascript required.
+ */
+ public $popup = null;
+
+ /**
+ * @var string the action to use for dialog destination. Default: the current route.
+ */
+ public $action = null;
+
+ /**
+ * @var boolean include the CSS file. Default is true.
+ * If this is set false, you are responsible to explicitly include the necessary CSS file in your page.
+ */
+ public $assetBundle = 'nodge\\eauth\\assets\\WidgetAssetBundle';
+
+ /**
+ * Initializes the widget.
+ * This method is called by {@link CBaseController::createWidget}
+ * and {@link CBaseController::beginWidget} after the widget's
+ * properties have been initialized.
+ */
+ public function init()
+ {
+ parent::init();
+
+ // EAuth component
+ /** @var $component \nodge\eauth\EAuth */
+ $component = Yii::$app->get($this->component);
+
+ // Some default properties from component configuration
+ if (!isset($this->services)) {
+ $this->services = $component->getServices();
+ }
+
+ if (is_array($this->predefinedServices)) {
+ $_services = [];
+ foreach ($this->predefinedServices as $_serviceName) {
+ if (isset($this->services[$_serviceName])) {
+ $_services[$_serviceName] = $this->services[$_serviceName];
+ }
+ }
+ $this->services = $_services;
+ }
+
+ if (!isset($this->popup)) {
+ $this->popup = $component->popup;
+ }
+
+ // Set the current route, if it is not set.
+ if (!isset($this->action)) {
+ $this->action = '/' . Yii::$app->requestedRoute;
+ }
+ }
+
+ /**
+ * Executes the widget.
+ * This method is called by {@link CBaseController::endWidget}.
+ */
+ public function run()
+ {
+ parent::run();
+ echo $this->render('widget', [
+ 'id' => $this->getId(),
+ 'services' => $this->services,
+ 'action' => $this->action,
+ 'popup' => $this->popup,
+ 'assetBundle' => $this->assetBundle,
+ ]);
+ }
+}
diff --git a/common/components/nodge/eauth/src/assets/WidgetAssetBundle.php b/common/components/nodge/eauth/src/assets/WidgetAssetBundle.php
new file mode 100644
index 0000000..d6481e2
--- /dev/null
+++ b/common/components/nodge/eauth/src/assets/WidgetAssetBundle.php
@@ -0,0 +1,28 @@
+
+ * @since 2.0
+ */
+class WidgetAssetBundle extends AssetBundle
+{
+ public $sourcePath = '@eauth/assets';
+ public $css = [
+ 'css/eauth.css',
+ ];
+ public $js = [
+ 'js/eauth.js',
+ ];
+ public $depends = [
+ 'yii\web\JqueryAsset',
+ ];
+}
diff --git a/common/components/nodge/eauth/src/assets/css/eauth.css b/common/components/nodge/eauth/src/assets/css/eauth.css
new file mode 100644
index 0000000..fb5cb52
--- /dev/null
+++ b/common/components/nodge/eauth/src/assets/css/eauth.css
@@ -0,0 +1,103 @@
+
+.eauth {
+ overflow: hidden;
+}
+
+.eauth-list {
+ margin: -1em 0 0;
+ padding: 0;
+ list-style: none;
+}
+
+.eauth-service {
+ display: inline-block;
+ vertical-align: top;
+ margin: 1em 1em 0 0;
+}
+
+.eauth-service-link {
+ position: relative;
+ display: block;
+ width: 60px;
+ padding-top: 34px;
+ text-align: center;
+}
+
+.eauth-service-link:before,
+.eauth-service-link:after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 50%;
+ width: 32px;
+ height: 32px;
+ margin-left: -16px;
+ background: url("../images/auth.png") 0 0 no-repeat;
+}
+
+.eauth-service-link:after {
+ display: none;
+}
+
+.eauth-service-link:hover:after {
+ display: block;
+}
+
+/* --- Services --- */
+
+.eauth-service-id-google_oauth .eauth-service-link:before {
+ background-position: 0 -34px;
+}
+
+.eauth-service-id-yandex_oauth .eauth-service-link:before {
+ background-position: 0 -102px;
+}
+
+.eauth-service-id-twitter .eauth-service-link:before {
+ background-position: 0 -68px;
+}
+
+.eauth-service-id-vkontakte .eauth-service-link:before {
+ background-position: 0 -136px;
+}
+
+.eauth-service-id-facebook .eauth-service-link:before {
+ background-position: 0 -170px;
+}
+
+.eauth-service-id-mailru .eauth-service-link:before {
+ background-position: 0 -204px;
+}
+
+.eauth-service-id-moikrug .eauth-service-link:before {
+ background-position: 0 -238px;
+}
+
+.eauth-service-id-odnoklassniki .eauth-service-link:before {
+ background-position: 0 -272px;
+}
+
+.eauth-service-id-linkedin .eauth-service-link:before,
+.eauth-service-id-linkedin_oauth2 .eauth-service-link:before {
+ background-position: 0 -306px;
+}
+
+.eauth-service-id-github .eauth-service-link:before {
+ background-position: 0 -340px;
+}
+
+.eauth-service-id-live .eauth-service-link:before {
+ background-position: 0 -372px;
+}
+
+.eauth-service-id-yahoo .eauth-service-link:before {
+ background-position: 0 -406px;
+}
+
+.eauth-service-id-steam .eauth-service-link:before {
+ background-position: 0 -440px;
+}
+
+.eauth-service-id-instagram .eauth-service-link:before {
+ background-position: 0 -474px;
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/assets/images/auth-src.png b/common/components/nodge/eauth/src/assets/images/auth-src.png
new file mode 100644
index 0000000..9c91275
Binary files /dev/null and b/common/components/nodge/eauth/src/assets/images/auth-src.png differ
diff --git a/common/components/nodge/eauth/src/assets/images/auth.png b/common/components/nodge/eauth/src/assets/images/auth.png
new file mode 100644
index 0000000..818f3aa
Binary files /dev/null and b/common/components/nodge/eauth/src/assets/images/auth.png differ
diff --git a/common/components/nodge/eauth/src/assets/js/eauth.js b/common/components/nodge/eauth/src/assets/js/eauth.js
new file mode 100644
index 0000000..52dfe22
--- /dev/null
+++ b/common/components/nodge/eauth/src/assets/js/eauth.js
@@ -0,0 +1,47 @@
+/*
+ * Yii EAuth extension.
+ * @author Maxim Zemskov
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+(function ($) {
+ var popup,
+ defaults = {
+ popup: {
+ width: 450,
+ height: 380
+ }
+ };
+
+ function openPopup(options) {
+ if (popup != null)
+ popup.close();
+
+ var redirect_uri,
+ url = redirect_uri = options.url;
+
+ url += url.indexOf('?') >= 0 ? '&' : '?';
+ if (url.indexOf('redirect_uri=') === -1)
+ url += 'redirect_uri=' + encodeURIComponent(redirect_uri) + '&';
+ url += 'js=';
+
+ var centerWidth = (window.screen.width - options.popup.width) / 2,
+ centerHeight = (window.screen.height - options.popup.height) / 2;
+
+ popup = window.open(url, "yii_eauth_popup", "width=" + options.popup.width + ",height=" + options.popup.height + ",left=" + centerWidth + ",top=" + centerHeight + ",resizable=yes,scrollbars=no,toolbar=no,menubar=no,location=no,directories=no,status=yes");
+ popup.focus();
+ }
+
+ $.fn.eauth = function (services) {
+ $(this).on('click', '[data-eauth-service]', function (e) {
+ e.preventDefault();
+
+ var service = $(this).data('eauthService'),
+ options = $.extend({
+ url: this.href
+ }, defaults, services[service]);
+
+ openPopup(options);
+ });
+ };
+})(jQuery);
diff --git a/common/components/nodge/eauth/src/messages/blank/eauth.php b/common/components/nodge/eauth/src/messages/blank/eauth.php
new file mode 100644
index 0000000..8a4309d
--- /dev/null
+++ b/common/components/nodge/eauth/src/messages/blank/eauth.php
@@ -0,0 +1,23 @@
+ '{service}',
+ 'Invalid response http code: {code}.' => '{code}',
+ 'Invalid response format.' => '',
+ 'Unable to complete the authentication because the required data was not received.' => '{provider}',
+ 'Unable to complete the request because the user was not authenticated.' => '',
+
+ 'Redirecting back to the application...' => '',
+ 'Click here to return to the application.' => '',
+
+ 'Google' => 'Google',
+ 'Twitter' => 'Twitter',
+ 'Yandex' => 'Yandex',
+ 'VK.com' => 'VK.com',
+ 'Facebook' => 'Facebook',
+ 'Mail.ru' => 'Mail.ru',
+ 'Moikrug.ru' => 'Moikrug.ru',
+ 'Odnoklassniki' => 'Odnoklassniki',
+ 'LinkedIn' => 'LinkedIn',
+ 'GitHub' => 'GitHub',
+];
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/messages/de/eauth.php b/common/components/nodge/eauth/src/messages/de/eauth.php
new file mode 100644
index 0000000..4c57ab5
--- /dev/null
+++ b/common/components/nodge/eauth/src/messages/de/eauth.php
@@ -0,0 +1,23 @@
+ 'Undefinierter Servicename: {service}',
+ 'Invalid response http code: {code}.' => 'Ungültiger HTTP Code: {code}',
+ 'Invalid response format.' => 'Ungültiges Antwortformat.',
+ 'Unable to complete the authentication because the required data was not received.' => 'Die Authentifizierung konnte nicht durchgeführt werden, da keine Daten empfangen wurden.',
+ 'Unable to complete the request because the user was not authenticated.' => 'Die Anfrage konnte nicht durchgeführt werden, da der Nutzer nicht authentifiziert war.',
+
+ 'Redirecting back to the application...' => 'Weiterleitung zur Applikation...',
+ 'Click here to return to the application.' => 'Klicke hier um zur Applikation zurückzukehren',
+
+ 'Google' => 'Google',
+ 'Twitter' => 'Twitter',
+ 'Yandex' => 'Yandex',
+ 'VK.com' => 'VK.com',
+ 'Facebook' => 'Facebook',
+ 'Mail.ru' => 'Mail.ru',
+ 'Moikrug.ru' => 'Moikrug.ru',
+ 'Odnoklassniki' => 'Odnoklassniki',
+ 'LinkedIn' => 'LinkedIn',
+ 'GitHub' => 'GitHub',
+];
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/messages/en/eauth.php b/common/components/nodge/eauth/src/messages/en/eauth.php
new file mode 100644
index 0000000..6ffa464
--- /dev/null
+++ b/common/components/nodge/eauth/src/messages/en/eauth.php
@@ -0,0 +1,23 @@
+ 'Undefined service name: {service}.',
+ 'Invalid response http code: {code}.' => 'Invalid response http code: {code}.',
+ 'Invalid response format.' => 'Invalid response format.',
+ 'Unable to complete the authentication because the required data was not received.' => 'Unable to complete the authentication because the required data was not received.',
+ 'Unable to complete the request because the user was not authenticated.' => 'Unable to complete the request because the user was not authenticated.',
+
+ 'Redirecting back to the application...' => 'Redirecting back to the application...',
+ 'Click here to return to the application.' => 'Click here to return to the application.',
+
+ 'Google' => 'Google',
+ 'Twitter' => 'Twitter',
+ 'Yandex' => 'Yandex',
+ 'VK.com' => 'VK.com',
+ 'Facebook' => 'Facebook',
+ 'Mail.ru' => 'Mail.ru',
+ 'Moikrug.ru' => 'Moikrug.ru',
+ 'Odnoklassniki' => 'Odnoklassniki',
+ 'LinkedIn' => 'LinkedIn',
+ 'GitHub' => 'GitHub',
+];
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/messages/ru/eauth.php b/common/components/nodge/eauth/src/messages/ru/eauth.php
new file mode 100644
index 0000000..067993b
--- /dev/null
+++ b/common/components/nodge/eauth/src/messages/ru/eauth.php
@@ -0,0 +1,23 @@
+ 'Авторизация с помощью {service} не поддерживается.',
+ 'Invalid response http code: {code}.' => 'Неверный ответ от сервера авторизации: {code}.',
+ 'Invalid response format.' => 'Сервер авторизации вернул данные в неправильном формате.',
+ 'Unable to complete the authentication because the required data was not received.' => 'Невозможно завершить авторизацию пользователя, потому что {provider} не передает необходимую информацию.',
+ 'Unable to complete the request because the user was not authenticated.' => 'Невозможно выполнить защищенный запрос, потому что пользователь не был авторизован.',
+
+ 'Redirecting back to the application...' => 'Перенаправление обратно в приложение...',
+ 'Click here to return to the application.' => 'Нажмите сюда чтобы вернуться обратно в приложение.',
+
+ 'Google' => 'Google',
+ 'Twitter' => 'Twitter',
+ 'Yandex' => 'Яндекс',
+ 'VK.com' => 'ВКонтакте',
+ 'Facebook' => 'Facebook',
+ 'Mail.ru' => 'Mail.ru',
+ 'Moikrug.ru' => 'Мой круг',
+ 'Odnoklassniki' => 'Одноклассники',
+ 'LinkedIn' => 'LinkedIn',
+ 'GitHub' => 'GitHub',
+];
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/messages/uk/eauth.php b/common/components/nodge/eauth/src/messages/uk/eauth.php
new file mode 100644
index 0000000..4e1ba82
--- /dev/null
+++ b/common/components/nodge/eauth/src/messages/uk/eauth.php
@@ -0,0 +1,23 @@
+ 'Авторизація за допомогою {service} не підтримується.',
+ 'Invalid response http code: {code}.' => 'Невірна відповідь від сервера авторизації: {code}.',
+ 'Invalid response format.' => 'Сервер авторизації повернув данні в невірному форматі.',
+ 'Unable to complete the authentication because the required data was not received.' => 'Неможливо завершити авторизацію користувача, так як {provider} не передає необхідну інформацію.',
+ 'Unable to complete the request because the user was not authenticated.' => 'Неможливо виконати захищений запит, так як користувач не був авторизований.',
+
+ 'Redirecting back to the application...' => 'Redirecting back to the application...',
+ 'Click here to return to the application.' => 'Click here to return to the application.',
+
+ 'Google' => 'Google',
+ 'Twitter' => 'Twitter',
+ 'Yandex' => 'Яндекс',
+ 'VK.com' => 'ВКонтакте',
+ 'Facebook' => 'Facebook',
+ 'Mail.ru' => 'Mail.ru',
+ 'Moikrug.ru' => 'Мой круг',
+ 'Odnoklassniki' => 'Одноклассники',
+ 'LinkedIn' => 'LinkedIn',
+ 'GitHub' => 'GitHub',
+];
diff --git a/common/components/nodge/eauth/src/oauth/HttpClient.php b/common/components/nodge/eauth/src/oauth/HttpClient.php
new file mode 100644
index 0000000..0e36f87
--- /dev/null
+++ b/common/components/nodge/eauth/src/oauth/HttpClient.php
@@ -0,0 +1,269 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\oauth;
+
+use Yii;
+use OAuth\Common\Http\Client\AbstractClient;
+use OAuth\Common\Http\Exception\TokenResponseException;
+use OAuth\Common\Http\Uri\UriInterface;
+
+/**
+ * Client implementation for cURL
+ */
+class HttpClient extends AbstractClient
+{
+
+ /**
+ * If true, explicitly sets cURL to use SSL version 3. Use this if cURL
+ * compiles with GnuTLS SSL.
+ *
+ * @var bool
+ */
+ protected $forceSSL3 = false;
+
+ /**
+ * If true and you are working in safe_mode environment or inside open_basedir
+ * it will use streams instead of curl.
+ *
+ * @var bool
+ */
+ protected $useStreamsFallback = false;
+
+ /**
+ * @var UriInterface
+ */
+ protected $endpoint;
+
+ /**
+ * @var mixed
+ */
+ protected $requestBody;
+
+ /**
+ * @var array
+ */
+ protected $extraHeaders = [];
+
+ /**
+ * @var string
+ */
+ protected $method = 'POST';
+
+ /**
+ * @param bool $force
+ */
+ public function setForceSSL3($force)
+ {
+ $this->forceSSL3 = $force;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getForceSSL3()
+ {
+ return $this->forceSSL3;
+ }
+
+ /**
+ * @param bool $useStreamsFallback
+ */
+ public function setUseStreamsFallback($useStreamsFallback)
+ {
+ $this->useStreamsFallback = $useStreamsFallback;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getUseStreamsFallback()
+ {
+ return $this->useStreamsFallback;
+ }
+
+ /**
+ * Any implementing HTTP providers should send a request to the provided endpoint with the parameters.
+ * They should return, in string form, the response body and throw an exception on error.
+ *
+ * @param UriInterface $endpoint
+ * @param mixed $requestBody
+ * @param array $extraHeaders
+ * @param string $method
+ * @return string
+ */
+ public function retrieveResponse(UriInterface $endpoint, $requestBody, array $extraHeaders = [], $method = 'POST')
+ {
+ $this->endpoint = $endpoint;
+ $this->requestBody = $requestBody;
+ $this->extraHeaders = $extraHeaders;
+ $this->method = $method;
+
+ if ($this->useStreamsFallback && !$this->allowFollowLocation()) {
+ return $this->streams();
+ }
+
+ return $this->curl();
+ }
+
+ /**
+ * @return bool
+ */
+ protected function allowFollowLocation()
+ {
+ return !ini_get('safe_mode') && !ini_get('open_basedir');
+ }
+
+ /**
+ *
+ */
+ protected function prepareRequest()
+ {
+ $this->method = strtoupper($this->method);
+ $this->normalizeHeaders($this->extraHeaders);
+
+ if ($this->method === 'GET' && !empty($this->requestBody)) {
+ throw new \InvalidArgumentException('No body expected for "GET" request.');
+ }
+
+ if (!isset($this->extraHeaders['Content-type']) && $this->method === 'POST' && is_array($this->requestBody)) {
+ $this->extraHeaders['Content-type'] = 'Content-type: application/x-www-form-urlencoded';
+ }
+
+ // Some of services requires User-Agent header (e.g. GitHub)
+ if (!isset($this->extraHeaders['User-Agent'])) {
+ $this->extraHeaders['User-Agent'] = 'User-Agent: yii2-eauth';
+ }
+
+ $this->extraHeaders['Host'] = 'Host: ' . $this->endpoint->getHost();
+ $this->extraHeaders['Connection'] = 'Connection: close';
+
+ if (YII_DEBUG) {
+ Yii::trace('EAuth http request: ' . PHP_EOL . var_export([
+ 'url' => $this->endpoint->getAbsoluteUri(),
+ 'method' => $this->method,
+ 'headers' => $this->extraHeaders,
+ 'body' => $this->requestBody,
+ ], true), __NAMESPACE__);
+ }
+
+ if (is_array($this->requestBody)) {
+ $this->requestBody = http_build_query($this->requestBody, null, '&');
+ }
+ }
+
+ /**
+ * @return string
+ * @throws TokenResponseException
+ */
+ protected function curl()
+ {
+ $this->prepareRequest();
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $this->endpoint->getAbsoluteUri());
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+
+ if ($this->method === 'POST' || $this->method === 'PUT') {
+ if ($this->method === 'PUT') {
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
+ } else {
+ curl_setopt($ch, CURLOPT_POST, true);
+ }
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody);
+ } else {
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
+ }
+
+ if ($this->allowFollowLocation() && $this->maxRedirects > 0) {
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($ch, CURLOPT_MAXREDIRS, $this->maxRedirects);
+ }
+
+ curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HEADER, false);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $this->extraHeaders);
+
+ if ($this->forceSSL3) {
+ curl_setopt($ch, CURLOPT_SSLVERSION, 3);
+ }
+
+ $response = curl_exec($ch);
+ $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+ if (YII_DEBUG) {
+ Yii::trace('EAuth http response: ' . PHP_EOL . var_export($response, true), __NAMESPACE__);
+ }
+
+ if (false === $response) {
+ $errNo = curl_errno($ch);
+ $errStr = curl_error($ch);
+ curl_close($ch);
+
+ if (empty($errStr)) {
+ $errStr = 'Failed to request resource.';
+ } else {
+ $errStr = 'cURL Error # ' . $errNo . ': ' . $errStr;
+ }
+
+ Yii::error('EAuth curl error (' . $responseCode . '): ' . $errStr, __NAMESPACE__);
+ throw new TokenResponseException($errStr, $responseCode);
+ }
+
+ curl_close($ch);
+
+ return $response;
+ }
+
+ /**
+ * @return string
+ * @throws TokenResponseException
+ */
+ protected function streams()
+ {
+ $this->prepareRequest();
+
+ $context = stream_context_create([
+ 'http' => [
+ 'method' => $this->method,
+ 'header' => array_values($this->extraHeaders),
+ 'content' => $this->requestBody,
+ 'protocol_version' => '1.1',
+ 'user_agent' => 'Yii2 EAuth Client',
+ 'max_redirects' => $this->maxRedirects,
+ 'timeout' => $this->timeout,
+ ],
+ ]);
+
+ $level = error_reporting(0);
+ $response = file_get_contents($this->endpoint->getAbsoluteUri(), false, $context);
+ error_reporting($level);
+
+ if (YII_DEBUG) {
+ Yii::trace('EAuth http response: ' . PHP_EOL . var_export($response, true), __NAMESPACE__);
+ }
+
+ if (false === $response) {
+ $lastError = error_get_last();
+
+ if (is_null($lastError)) {
+ $errStr = 'Failed to request resource.';
+ } else {
+ $errStr = $lastError['message'];
+ }
+
+ Yii::error('EAuth streams error: ' . $errStr, __NAMESPACE__);
+ throw new TokenResponseException($errStr);
+ }
+
+ return $response;
+ }
+
+}
diff --git a/common/components/nodge/eauth/src/oauth/ServiceBase.php b/common/components/nodge/eauth/src/oauth/ServiceBase.php
new file mode 100644
index 0000000..26f9126
--- /dev/null
+++ b/common/components/nodge/eauth/src/oauth/ServiceBase.php
@@ -0,0 +1,313 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\oauth;
+
+use Yii;
+use OAuth\Common\Http\Uri\Uri;
+use OAuth\Common\Http\Client\ClientInterface;
+use OAuth\Common\Token\TokenInterface;
+use OAuth\Common\Storage\TokenStorageInterface;
+use nodge\eauth\EAuth;
+use nodge\eauth\IAuthService;
+use nodge\eauth\ErrorException;
+use yii\helpers\ArrayHelper;
+use yii\helpers\Url;
+
+/**
+ * EOAuthService is a base class for all OAuth providers.
+ *
+ * @package application.extensions.eauth
+ */
+abstract class ServiceBase extends \nodge\eauth\ServiceBase implements IAuthService
+{
+
+ /**
+ * @var string Base url for API calls.
+ */
+ protected $baseApiUrl;
+
+ /**
+ * @var int Default token lifetime. Used when service wont provide expires_in param.
+ */
+ protected $tokenDefaultLifetime = null;
+
+ /**
+ * @var array TokenStorage class. Null means default value from EAuth component config.
+ */
+ protected $tokenStorage;
+
+ /**
+ * @var array HttpClient class. Null means default value from EAuth component config.
+ */
+ protected $httpClient;
+
+ /**
+ * @var TokenStorageInterface
+ */
+ private $_tokenStorage;
+
+ /**
+ * @var ClientInterface
+ */
+ private $_httpClient;
+
+ /**
+ * Initialize the component.
+ *
+ * @param EAuth $component the component instance.
+ * @param array $options properties initialization.
+ */
+// public function init($component, $options = []) {
+// parent::init($component, $options);
+// }
+
+ /**
+ * For OAuth we can check existing access token.
+ * Useful for API calls.
+ *
+ * @return bool
+ * @throws ErrorException
+ */
+ public function getIsAuthenticated()
+ {
+ if (!$this->authenticated) {
+ try {
+ $proxy = $this->getProxy();
+ $this->authenticated = $proxy->hasValidAccessToken();
+ } catch (\OAuth\Common\Exception\Exception $e) {
+ throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e);
+ }
+ }
+ return parent::getIsAuthenticated();
+ }
+
+ /**
+ * @return \nodge\eauth\oauth1\ServiceProxy|\nodge\eauth\oauth2\ServiceProxy
+ */
+ abstract protected function getProxy();
+
+ /**
+ * @return string the current url
+ */
+ protected function getCallbackUrl()
+ {
+ return Url::to('', true);
+ }
+
+ /**
+ * @param array $config
+ */
+ public function setTokenStorage(array $config)
+ {
+ $this->tokenStorage = ArrayHelper::merge($this->tokenStorage, $config);
+ }
+
+ /**
+ * @return TokenStorageInterface
+ */
+ protected function getTokenStorage()
+ {
+ if (!isset($this->_tokenStorage)) {
+ $config = $this->tokenStorage;
+ if (!isset($config)) {
+ $config = $this->getComponent()->getTokenStorage();
+ }
+ $this->_tokenStorage = Yii::createObject($config);
+ }
+ return $this->_tokenStorage;
+ }
+
+ /**
+ * @param array $config
+ */
+ public function setHttpClient(array $config)
+ {
+ $this->httpClient = ArrayHelper::merge($this->httpClient, $config);
+ }
+
+ /**
+ * @return ClientInterface
+ */
+ protected function getHttpClient()
+ {
+ if (!isset($this->_httpClient)) {
+ $config = $this->httpClient;
+ if (!isset($config)) {
+ $config = $this->getComponent()->getHttpClient();
+ }
+ $this->_httpClient = Yii::createObject($config);
+ }
+ return $this->_httpClient;
+ }
+
+ /**
+ * @return int
+ */
+ public function getTokenDefaultLifetime()
+ {
+ return $this->tokenDefaultLifetime;
+ }
+
+ /**
+ * Returns the protected resource.
+ *
+ * @param string $url url to request.
+ * @param array $options HTTP request options. Keys: query, data, headers.
+ * @param boolean $parseResponse Whether to parse response.
+ * @return mixed the response.
+ * @throws ErrorException
+ */
+ public function makeSignedRequest($url, $options = [], $parseResponse = true)
+ {
+ if (!$this->getIsAuthenticated()) {
+ throw new ErrorException(Yii::t('eauth', 'Unable to complete the signed request because the user was not authenticated.'), 401);
+ }
+
+ if (stripos($url, 'http') !== 0) {
+ $url = $this->baseApiUrl . $url;
+ }
+
+ $url = new Uri($url);
+ if (isset($options['query'])) {
+ foreach ($options['query'] as $key => $value) {
+ $url->addToQuery($key, $value);
+ }
+ }
+
+ $data = isset($options['data']) ? $options['data'] : [];
+ $method = !empty($data) ? 'POST' : 'GET';
+ $headers = isset($options['headers']) ? $options['headers'] : [];
+
+ $response = $this->getProxy()->request($url, $method, $data, $headers);
+
+ if ($parseResponse) {
+ $response = $this->parseResponseInternal($response);
+ }
+
+ return $response;
+ }
+
+ /**
+ * Returns the public resource.
+ *
+ * @param string $url url to request.
+ * @param array $options HTTP request options. Keys: query, data, headers.
+ * @param boolean $parseResponse Whether to parse response.
+ * @return mixed the response.
+ */
+ public function makeRequest($url, $options = [], $parseResponse = true) {
+ if (stripos($url, 'http') !== 0) {
+ $url = $this->baseApiUrl . $url;
+ }
+
+ $url = new Uri($url);
+ if (isset($options['query'])) {
+ foreach ($options['query'] as $key => $value) {
+ $url->addToQuery($key, $value);
+ }
+ }
+
+ $data = isset($options['data']) ? $options['data'] : [];
+ $method = !empty($data) ? 'POST' : 'GET';
+
+ $headers = isset($options['headers']) ? $options['headers'] : [];
+ $headers = array_merge($this->getProxy()->getExtraApiHeaders(), $headers);
+
+ $response = $this->getHttpClient()->retrieveResponse($url, $data, $headers, $method);
+ if ($parseResponse) {
+ $response = $this->parseResponseInternal($response);
+ }
+
+ return $response;
+ }
+
+ /**
+ * Parse response and check for errors.
+ *
+ * @param string $response
+ * @return mixed
+ * @throws ErrorException
+ */
+ protected function parseResponseInternal($response)
+ {
+ try {
+ $result = $this->parseResponse($response);
+ if (!isset($result)) {
+ throw new ErrorException(Yii::t('eauth', 'Invalid response format.'), 500);
+ }
+
+ $error = $this->fetchResponseError($result);
+ if (isset($error) && !empty($error['message'])) {
+ throw new ErrorException($error['message'], $error['code']);
+ }
+
+ return $result;
+ } catch (\Exception $e) {
+ throw new ErrorException($e->getMessage(), $e->getCode());
+ }
+ }
+
+ /**
+ * @param string $response
+ * @return mixed
+ */
+ protected function parseResponse($response)
+ {
+ return json_decode($response, true);
+ }
+
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['error'])) {
+ return [
+ 'code' => 500,
+ 'message' => 'Unknown error occurred.',
+ ];
+ }
+ return null;
+ }
+
+ /**
+ * @return array|null An array with valid access_token information.
+ */
+ protected function getAccessTokenData()
+ {
+ if (!$this->getIsAuthenticated()) {
+ return null;
+ }
+
+ $token = $this->getProxy()->getAccessToken();
+ if (!isset($token)) {
+ return null;
+ }
+
+ return [
+ 'access_token' => $token->getAccessToken(),
+ 'refresh_token' => $token->getRefreshToken(),
+ 'expires' => $token->getEndOfLife(),
+ 'params' => $token->getExtraParams(),
+ ];
+ }
+
+ /**
+ * @param array $data
+ * @return string|null
+ */
+ public function getAccessTokenResponseError($data)
+ {
+ return isset($data['error']) ? $data['error'] : null;
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/oauth/SessionTokenStorage.php b/common/components/nodge/eauth/src/oauth/SessionTokenStorage.php
new file mode 100644
index 0000000..ffc5300
--- /dev/null
+++ b/common/components/nodge/eauth/src/oauth/SessionTokenStorage.php
@@ -0,0 +1,177 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\oauth;
+
+use Yii;
+use OAuth\Common\Storage\TokenStorageInterface;
+use OAuth\Common\Token\TokenInterface;
+use OAuth\Common\Storage\Exception\TokenNotFoundException;
+use OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException;
+
+/**
+ * Stores a token in a PHP session.
+ */
+class SessionTokenStorage implements TokenStorageInterface
+{
+
+ const SESSION_TOKEN_PREFIX = 'eauth-token-';
+ const SESSION_STATE_PREFIX = 'eauth-state-';
+
+ /**
+ * @var
+ */
+ protected $componentName;
+
+ /**
+ * @param string $componentName
+ */
+ public function __construct($componentName = 'session')
+ {
+ $this->componentName = $componentName;
+ }
+
+ /**
+ * @return null|object
+ */
+ protected function getSession()
+ {
+ return Yii::$app->get($this->componentName);
+ }
+
+ /**
+ * @param string $service
+ * @return TokenInterface
+ * @throws TokenNotFoundException
+ */
+ public function retrieveAccessToken($service)
+ {
+ if ($this->hasAccessToken($service)) {
+ return $this->getSession()->get(self::SESSION_TOKEN_PREFIX . $service);
+ }
+ throw new TokenNotFoundException('Token not found in session, are you sure you stored it?');
+ }
+
+ /**
+ * @param string $service
+ * @param TokenInterface $token
+ * @return TokenInterface
+ */
+ public function storeAccessToken($service, TokenInterface $token)
+ {
+ $this->getSession()->set(self::SESSION_TOKEN_PREFIX . $service, $token);
+ return $token;
+ }
+
+ /**
+ * @param string $service
+ * @return bool
+ */
+ public function hasAccessToken($service)
+ {
+ return $this->getSession()->has(self::SESSION_TOKEN_PREFIX . $service);
+ }
+
+ /**
+ * Delete the users token. Aka, log out.
+ *
+ * @param string $service
+ * @return TokenStorageInterface
+ */
+ public function clearToken($service)
+ {
+ $this->getSession()->remove(self::SESSION_TOKEN_PREFIX . $service);
+ return $this;
+ }
+
+ /**
+ * Delete *ALL* user tokens.
+ *
+ * @return TokenStorageInterface
+ */
+ public function clearAllTokens()
+ {
+ $session = $this->getSession();
+ foreach ($session as $key => $value) {
+ if (strpos($key, self::SESSION_TOKEN_PREFIX) === 0) {
+ $session->remove($key);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Store the authorization state related to a given service
+ *
+ * @param string $service
+ * @param string $state
+ * @return TokenStorageInterface
+ */
+ public function storeAuthorizationState($service, $state)
+ {
+ $this->getSession()->set(self::SESSION_STATE_PREFIX . $service, $state);
+ return $this;
+ }
+
+ /**
+ * Check if an authorization state for a given service exists
+ *
+ * @param string $service
+ * @return bool
+ */
+ public function hasAuthorizationState($service)
+ {
+ return $this->getSession()->has(self::SESSION_STATE_PREFIX . $service);
+ }
+
+ /**
+ * Retrieve the authorization state for a given service
+ *
+ * @param string $service
+ * @return string
+ * @throws AuthorizationStateNotFoundException
+ */
+ public function retrieveAuthorizationState($service)
+ {
+ if ($this->hasAuthorizationState($service)) {
+ return $this->getSession()->get(self::SESSION_STATE_PREFIX . $service);
+ }
+ throw new AuthorizationStateNotFoundException('State not found in session, are you sure you stored it?');
+ }
+
+ /**
+ * Clear the authorization state of a given service
+ *
+ * @param string $service
+ * @return TokenStorageInterface
+ */
+ public function clearAuthorizationState($service)
+ {
+ $this->getSession()->remove(self::SESSION_STATE_PREFIX . $service);
+ return $this;
+ }
+
+ /**
+ * Delete *ALL* user authorization states. Use with care. Most of the time you will likely
+ * want to use clearAuthorizationState() instead.
+ *
+ * @return TokenStorageInterface
+ */
+ public function clearAllAuthorizationStates()
+ {
+ $session = $this->getSession();
+ foreach ($session as $key => $value) {
+ if (strpos($key, self::SESSION_STATE_PREFIX) === 0) {
+ $session->remove($key);
+ }
+ }
+ return $this;
+ }
+
+}
diff --git a/common/components/nodge/eauth/src/oauth1/Service.php b/common/components/nodge/eauth/src/oauth1/Service.php
new file mode 100644
index 0000000..78064b5
--- /dev/null
+++ b/common/components/nodge/eauth/src/oauth1/Service.php
@@ -0,0 +1,164 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\oauth1;
+
+use Yii;
+use OAuth\Common\Exception\Exception as OAuthException;
+use OAuth\Common\Http\Uri\Uri;
+use OAuth\Common\Consumer\Credentials;
+use OAuth\OAuth1\Signature\Signature;
+use nodge\eauth\EAuth;
+use nodge\eauth\ErrorException;
+use nodge\eauth\IAuthService;
+use nodge\eauth\oauth\ServiceBase;
+
+/**
+ * EOAuthService is a base class for all OAuth providers.
+ *
+ * @package application.extensions.eauth
+ */
+abstract class Service extends ServiceBase implements IAuthService
+{
+
+ /**
+ * @var string OAuth2 client id.
+ */
+ protected $key;
+
+ /**
+ * @var string OAuth2 client secret key.
+ */
+ protected $secret;
+
+ /**
+ * @var array Provider options. Must contain the keys: request, authorize, access.
+ */
+ protected $providerOptions = [
+ 'request' => '',
+ 'authorize' => '',
+ 'access' => '',
+ ];
+
+ /**
+ * @var ServiceProxy
+ */
+ private $_proxy;
+
+ /**
+ * Initialize the component.
+ *
+ * @param EAuth $component the component instance.
+ * @param array $options properties initialization.
+ */
+// public function init($component, $options = []) {
+// parent::init($component, $options);
+// }
+
+ /**
+ * @param string $key
+ */
+ public function setKey($key)
+ {
+ $this->key = $key;
+ }
+
+ /**
+ * @param string $secret
+ */
+ public function setSecret($secret)
+ {
+ $this->secret = $secret;
+ }
+
+ /**
+ * @return ServiceProxy
+ */
+ protected function getProxy()
+ {
+ if (!isset($this->_proxy)) {
+ $storage = $this->getTokenStorage();
+ $httpClient = $this->getHttpClient();
+ $credentials = new Credentials($this->key, $this->secret, $this->getCallbackUrl());
+ $signature = new Signature($credentials);
+ $this->_proxy = new ServiceProxy($credentials, $httpClient, $storage, $signature, null, $this);
+ }
+ return $this->_proxy;
+ }
+
+ /**
+ * Authenticate the user.
+ *
+ * @return boolean whether user was successfuly authenticated.
+ * @throws ErrorException
+ */
+ public function authenticate()
+ {
+ try {
+ $proxy = $this->getProxy();
+
+ if (!empty($_GET['oauth_token'])) {
+ $token = $proxy->retrieveAccessToken();
+
+ // This was a callback request, get the token now
+ $proxy->requestAccessToken($_GET['oauth_token'], $_GET['oauth_verifier'], $token->getRequestTokenSecret());
+
+ $this->authenticated = true;
+ } else if ($proxy->hasValidAccessToken()) {
+ $this->authenticated = true;
+ } else {
+ // extra request needed for oauth1 to request a request token :-)
+ $token = $proxy->requestRequestToken();
+ /** @var $url Uri */
+ $url = $proxy->getAuthorizationUri(['oauth_token' => $token->getRequestToken()]);
+ Yii::$app->getResponse()->redirect($url->getAbsoluteUri())->send();
+ }
+ } catch (OAuthException $e) {
+ throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e);
+ }
+
+ return $this->getIsAuthenticated();
+ }
+
+ /**
+ * @return string
+ */
+ public function getRequestTokenEndpoint()
+ {
+ return $this->providerOptions['request'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getAuthorizationEndpoint()
+ {
+ return $this->providerOptions['authorize'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getAccessTokenEndpoint()
+ {
+ return $this->providerOptions['access'];
+ }
+
+ /**
+ * @return array
+ */
+ public function getAccessTokenArgumentNames()
+ {
+ return [
+ 'oauth_token' => 'oauth_token',
+ 'oauth_token_secret' => 'oauth_token_secret',
+ 'oauth_expires_in' => 'oauth_expires_in',
+ ];
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/oauth1/ServiceProxy.php b/common/components/nodge/eauth/src/oauth1/ServiceProxy.php
new file mode 100644
index 0000000..330f91a
--- /dev/null
+++ b/common/components/nodge/eauth/src/oauth1/ServiceProxy.php
@@ -0,0 +1,198 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\oauth1;
+
+use OAuth\Common\Consumer\CredentialsInterface;
+use OAuth\Common\Http\Client\ClientInterface;
+use OAuth\Common\Http\Exception\TokenResponseException;
+use OAuth\Common\Http\Uri\Uri;
+use OAuth\Common\Http\Uri\UriInterface;
+use OAuth\Common\Storage\TokenStorageInterface;
+use OAuth\Common\Token\TokenInterface;
+use OAuth\OAuth1\Service\AbstractService;
+use OAuth\OAuth1\Signature\SignatureInterface;
+use OAuth\OAuth1\Token\StdOAuth1Token;
+
+class ServiceProxy extends AbstractService
+{
+
+ /**
+ * @var Service the currently used service class
+ */
+ protected $service;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function __construct(
+ CredentialsInterface $credentials,
+ ClientInterface $httpClient,
+ TokenStorageInterface $storage,
+ SignatureInterface $signature,
+ UriInterface $baseApiUri = null,
+ Service $service
+ )
+ {
+ $this->service = $service;
+ parent::__construct($credentials, $httpClient, $storage, $signature, $baseApiUri);
+ }
+
+ /**
+ * @return string
+ */
+ public function service()
+ {
+ return $this->service->getServiceName();
+ }
+
+ /**
+ * @return StdOAuth1Token
+ */
+ public function retrieveAccessToken()
+ {
+ return $this->storage->retrieveAccessToken($this->service());
+ }
+
+ /**
+ *
+ */
+ public function hasValidAccessToken()
+ {
+ $serviceName = $this->service();
+
+ if (!$this->storage->hasAccessToken($serviceName)) {
+ return false;
+ }
+
+ /** @var $token StdOAuth1Token */
+ $token = $this->storage->retrieveAccessToken($serviceName);
+
+ $params = $token->getExtraParams();
+ if (isset($params['is_request_token'])) {
+ return false;
+ }
+
+ return $this->checkTokenLifetime($token);
+ }
+
+ /**
+ * @param TokenInterface $token
+ * @return bool
+ */
+ protected function checkTokenLifetime($token)
+ {
+ // assume that we have at least a minute to execute a queries.
+ return $token->getEndOfLife() - 60 > time()
+ || $token->getEndOfLife() === TokenInterface::EOL_NEVER_EXPIRES
+ || $token->getEndOfLife() === TokenInterface::EOL_UNKNOWN;
+ }
+
+ /**
+ * @return null|TokenInterface
+ */
+ public function getAccessToken()
+ {
+ if (!$this->hasValidAccessToken()) {
+ return null;
+ }
+
+ $serviceName = $this->service();
+ return $this->storage->retrieveAccessToken($serviceName);
+ }
+
+ /**
+ * @return UriInterface
+ */
+ public function getRequestTokenEndpoint()
+ {
+ return new Uri($this->service->getRequestTokenEndpoint());
+ }
+
+ /**
+ * @return UriInterface
+ */
+ public function getAuthorizationEndpoint()
+ {
+ return new Uri($this->service->getAuthorizationEndpoint());
+ }
+
+ /**
+ * @return UriInterface
+ */
+ public function getAccessTokenEndpoint()
+ {
+ return new Uri($this->service->getAccessTokenEndpoint());
+ }
+
+ /**
+ * We need a separate request token parser only to verify the `oauth_callback_confirmed` parameter. For the actual
+ * parsing we can just use the default access token parser.
+ *
+ * @param string $responseBody
+ * @return StdOAuth1Token
+ * @throws TokenResponseException
+ */
+ protected function parseRequestTokenResponse($responseBody)
+ {
+ parse_str($responseBody, $data);
+
+ if (!isset($data) || !is_array($data)) {
+ throw new TokenResponseException('Unable to parse response.');
+ } else if (!isset($data['oauth_callback_confirmed']) || $data['oauth_callback_confirmed'] != 'true') {
+ throw new TokenResponseException('Error in retrieving token.');
+ }
+
+ $data['is_request_token'] = true;
+ return $this->parseAccessTokenResponse($data);
+ }
+
+ /**
+ * @param string|array $responseBody
+ * @return StdOAuth1Token
+ * @throws TokenResponseException
+ */
+ protected function parseAccessTokenResponse($responseBody)
+ {
+ if (!is_array($responseBody)) {
+ parse_str($responseBody, $data);
+
+ if (!isset($data) || !is_array($data)) {
+ throw new TokenResponseException('Unable to parse response.');
+ }
+ } else {
+ $data = $responseBody;
+ }
+
+ $error = $this->service->getAccessTokenResponseError($data);
+ if (isset($error)) {
+ throw new TokenResponseException('Error in retrieving token: "' . $error . '"');
+ }
+
+ $token = new StdOAuth1Token();
+ $names = $this->service->getAccessTokenArgumentNames();
+
+ $token->setRequestToken($data[$names['oauth_token']]);
+ $token->setRequestTokenSecret($data[$names['oauth_token_secret']]);
+ $token->setAccessToken($data[$names['oauth_token']]);
+ $token->setAccessTokenSecret($data[$names['oauth_token_secret']]);
+ unset($data[$names['oauth_token']], $data[$names['oauth_token_secret']]);
+
+ if (isset($data[$names['oauth_expires_in']])) {
+ $token->setLifeTime($data[$names['oauth_expires_in']]);
+ unset($data[$names['oauth_expires_in']]);
+ } else {
+ $token->setLifetime($this->service->getTokenDefaultLifetime());
+ }
+
+ $token->setExtraParams($data);
+
+ return $token;
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/oauth2/Service.php b/common/components/nodge/eauth/src/oauth2/Service.php
new file mode 100644
index 0000000..f42bb16
--- /dev/null
+++ b/common/components/nodge/eauth/src/oauth2/Service.php
@@ -0,0 +1,337 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\oauth2;
+
+use Yii;
+use yii\helpers\Url;
+use OAuth\Common\Exception\Exception as OAuthException;
+use OAuth\Common\Http\Uri\Uri;
+use OAuth\Common\Consumer\Credentials;
+use OAuth\OAuth2\Service\ServiceInterface;
+use nodge\eauth\EAuth;
+use nodge\eauth\ErrorException;
+use nodge\eauth\IAuthService;
+use nodge\eauth\oauth\ServiceBase;
+
+/**
+ * EOAuthService is a base class for all OAuth providers.
+ *
+ * @package application.extensions.eauth
+ */
+abstract class Service extends ServiceBase implements IAuthService
+{
+
+ /**
+ * @var string OAuth2 client id.
+ */
+ protected $clientId;
+
+ /**
+ * @var string OAuth2 client secret key.
+ */
+ protected $clientSecret;
+
+ /**
+ * @var array OAuth scopes.
+ */
+ protected $scopes = [];
+
+ /**
+ * @var string
+ */
+ protected $scopeSeparator = ' ';
+
+ /**
+ * @var array Provider options. Must contain the keys: authorize, access_token.
+ */
+ protected $providerOptions = [
+ 'authorize' => '',
+ 'access_token' => '',
+ ];
+
+ /**
+ * @var string Error key name in _GET options.
+ */
+ protected $errorParam = 'error';
+
+ /**
+ * @var string Error description key name in _GET options.
+ */
+ protected $errorDescriptionParam = 'error_description';
+
+ /**
+ * @var string Error code for access_denied response.
+ */
+ protected $errorAccessDeniedCode = 'access_denied';
+
+ /**
+ * @var string The display name for popup window. False to disable display mode.
+ */
+ protected $popupDisplayName = 'popup';
+
+ /**
+ * @var bool Whether to use the State param to improve security.
+ */
+ protected $validateState = true;
+
+ /**
+ * @var ServiceProxy
+ */
+ private $_proxy;
+
+ /**
+ * Initialize the component.
+ *
+ * @param EAuth $component the component instance.
+ * @param array $options properties initialization.
+ */
+// public function init($component, $options = []) {
+// parent::init($component, $options);
+// }
+
+ /**
+ * @param string $id
+ */
+ public function setClientId($id)
+ {
+ $this->clientId = $id;
+ }
+
+ /**
+ * @param string $secret
+ */
+ public function setClientSecret($secret)
+ {
+ $this->clientSecret = $secret;
+ }
+
+ /**
+ * @param string|array $scopes
+ */
+ public function setScope($scopes)
+ {
+ if (!is_array($scopes)) {
+ $scopes = [$scopes];
+ }
+
+ $resolvedScopes = [];
+ $reflClass = new \ReflectionClass($this);
+ $constants = $reflClass->getConstants();
+
+ foreach ($scopes as $scope) {
+ $key = strtoupper('SCOPE_' . $scope);
+
+ // try to find a class constant with this name
+ if (array_key_exists($key, $constants)) {
+ $resolvedScopes[] = $constants[$key];
+ } else {
+ $resolvedScopes[] = $scope;
+ }
+ }
+
+ $this->scopes = $resolvedScopes;
+ }
+
+ /**
+ * @param bool $validate
+ */
+ public function setValidateState($validate)
+ {
+ $this->validateState = $validate;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getValidateState()
+ {
+ return $this->validateState;
+ }
+
+ /**
+ * @return ServiceProxy
+ */
+ protected function getProxy()
+ {
+ if (!isset($this->_proxy)) {
+ $tokenStorage = $this->getTokenStorage();
+ $httpClient = $this->getHttpClient();
+ $credentials = new Credentials($this->clientId, $this->clientSecret, $this->getCallbackUrl());
+ $this->_proxy = new ServiceProxy($credentials, $httpClient, $tokenStorage, $this->scopes, null, $this);
+ }
+ return $this->_proxy;
+ }
+
+ /**
+ * @return string the current url
+ */
+ protected function getCallbackUrl()
+ {
+ if (isset($_GET['redirect_uri'])) {
+ $url = $_GET['redirect_uri'];
+ }
+ else {
+ $route = Yii::$app->getRequest()->getQueryParams();
+ array_unshift($route, '');
+
+ // Can not use these params in OAuth2 callbacks
+ foreach (['code', 'state', 'redirect_uri'] as $param) {
+ if (isset($route[$param])) {
+ unset($route[$param]);
+ }
+ }
+
+ $url = Url::to($route, true);
+ }
+
+ return $url;
+ }
+
+ /**
+ * Authenticate the user.
+ *
+ * @return boolean whether user was successfuly authenticated.
+ * @throws ErrorException
+ */
+ public function authenticate()
+ {
+ if (!$this->checkError()) {
+ return false;
+ }
+
+ try {
+ $proxy = $this->getProxy();
+
+ if (!empty($_GET['code'])) {
+ // This was a callback request from a service, get the token
+ $proxy->requestAccessToken($_GET['code']);
+ $this->authenticated = true;
+ } else if ($proxy->hasValidAccessToken()) {
+ $this->authenticated = true;
+ } else {
+ /** @var $url Uri */
+ $url = $proxy->getAuthorizationUri();
+ Yii::$app->getResponse()->redirect($url->getAbsoluteUri())->send();
+ }
+ } catch (OAuthException $e) {
+ throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e);
+ }
+
+ return $this->getIsAuthenticated();
+ }
+
+ /**
+ * Check request params for error code and message.
+ *
+ * @return bool
+ * @throws ErrorException
+ */
+ protected function checkError()
+ {
+ if (isset($_GET[$this->errorParam])) {
+ $error_code = $_GET[$this->errorParam];
+ if ($error_code === $this->errorAccessDeniedCode) {
+ // access_denied error (user canceled)
+ $this->cancel();
+ } else {
+ $error = $error_code;
+ if (isset($_GET[$this->errorDescriptionParam])) {
+ $error = $_GET[$this->errorDescriptionParam] . ' (' . $error . ')';
+ }
+ throw new ErrorException($error);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAuthorizationEndpoint()
+ {
+ $url = $this->providerOptions['authorize'];
+ if ($this->popupDisplayName !== false && $this->getIsInsidePopup()) {
+ $url = new Uri($url);
+ $url->addToQuery('display', $this->popupDisplayName);
+ $url = $url->getAbsoluteUri();
+ }
+ return $url;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAccessTokenEndpoint()
+ {
+ return $this->providerOptions['access_token'];
+ }
+
+ /**
+ * @param string $response
+ * @return array
+ */
+ public function parseAccessTokenResponse($response)
+ {
+ return json_decode($response, true);
+ }
+
+ /**
+ * @return array
+ */
+ public function getAccessTokenArgumentNames()
+ {
+ return [
+ 'access_token' => 'access_token',
+ 'expires_in' => 'expires_in',
+ 'refresh_token' => 'refresh_token',
+ ];
+ }
+
+ /**
+ * Return any additional headers always needed for this service implementation's OAuth calls.
+ *
+ * @return array
+ */
+ public function getExtraOAuthHeaders()
+ {
+ return [];
+ }
+
+ /**
+ * Return any additional headers always needed for this service implementation's API calls.
+ *
+ * @return array
+ */
+ public function getExtraApiHeaders()
+ {
+ return [];
+ }
+
+ /**
+ * Returns a class constant from ServiceInterface defining the authorization method used for the API
+ * Header is the sane default.
+ *
+ * @return int
+ */
+ public function getAuthorizationMethod()
+ {
+ return ServiceInterface::AUTHORIZATION_METHOD_HEADER_OAUTH;
+ }
+
+ /**
+ * @return string
+ */
+ public function getScopeSeparator()
+ {
+ return $this->scopeSeparator;
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/oauth2/ServiceProxy.php b/common/components/nodge/eauth/src/oauth2/ServiceProxy.php
new file mode 100644
index 0000000..a85efa2
--- /dev/null
+++ b/common/components/nodge/eauth/src/oauth2/ServiceProxy.php
@@ -0,0 +1,242 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\oauth2;
+
+use OAuth\Common\Consumer\CredentialsInterface;
+use OAuth\Common\Http\Client\ClientInterface;
+use OAuth\Common\Http\Exception\TokenResponseException;
+use OAuth\Common\Http\Uri\Uri;
+use OAuth\Common\Http\Uri\UriInterface;
+use OAuth\Common\Storage\TokenStorageInterface;
+use OAuth\Common\Token\TokenInterface;
+use OAuth\OAuth2\Service\AbstractService;
+use OAuth\OAuth2\Token\StdOAuth2Token;
+
+class ServiceProxy extends AbstractService
+{
+
+ /**
+ * @var Service the currently used service class
+ */
+ protected $service;
+
+ /**
+ * @param CredentialsInterface $credentials
+ * @param ClientInterface $httpClient
+ * @param TokenStorageInterface $storage
+ * @param array $scopes
+ * @param UriInterface $baseApiUri
+ * @param Service $service
+ */
+ public function __construct(
+ CredentialsInterface $credentials,
+ ClientInterface $httpClient,
+ TokenStorageInterface $storage,
+ $scopes = [],
+ UriInterface $baseApiUri = null,
+ Service $service
+ )
+ {
+ $this->service = $service;
+ parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri, $service->getValidateState());
+ }
+
+ /**
+ * @return string
+ */
+ public function service()
+ {
+ return $this->service->getServiceName();
+ }
+
+ /**
+ * Validate scope
+ *
+ * @param string $scope
+ * @return bool
+ */
+ public function isValidScope($scope)
+ {
+ $reflectionClass = new \ReflectionClass(get_class($this->service));
+ return in_array($scope, $reflectionClass->getConstants(), true);
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasValidAccessToken()
+ {
+ $serviceName = $this->service();
+
+ if (!$this->storage->hasAccessToken($serviceName)) {
+ return false;
+ }
+
+ /** @var $token StdOAuth2Token */
+ $token = $this->storage->retrieveAccessToken($serviceName);
+ $valid = $this->checkTokenLifetime($token);
+
+ if (!$valid) {
+ $refreshToken = $token->getRefreshToken();
+ if (isset($refreshToken)) {
+ $token = $this->refreshAccessToken($token);
+ return $this->checkTokenLifetime($token);
+ }
+ }
+
+ return $valid;
+ }
+
+ /**
+ * @param TokenInterface $token
+ * @return bool
+ */
+ protected function checkTokenLifetime($token)
+ {
+ // assume that we have at least a minute to execute a queries.
+ return $token->getEndOfLife() - 60 > time()
+ || $token->getEndOfLife() === TokenInterface::EOL_NEVER_EXPIRES
+ || $token->getEndOfLife() === TokenInterface::EOL_UNKNOWN;
+ }
+
+ /**
+ * @return null|TokenInterface
+ */
+ public function getAccessToken()
+ {
+ if (!$this->hasValidAccessToken()) {
+ return null;
+ }
+
+ $serviceName = $this->service();
+ return $this->storage->retrieveAccessToken($serviceName);
+ }
+
+ /**
+ * @return UriInterface
+ */
+ public function getAuthorizationEndpoint()
+ {
+ return new Uri($this->service->getAuthorizationEndpoint());
+ }
+
+ /**
+ * @return UriInterface
+ */
+ public function getAccessTokenEndpoint()
+ {
+ return new Uri($this->service->getAccessTokenEndpoint());
+ }
+
+ /**
+ * @param string $responseBody
+ * @return StdOAuth2Token
+ * @throws TokenResponseException
+ */
+ protected function parseAccessTokenResponse($responseBody)
+ {
+ $data = $this->service->parseAccessTokenResponse($responseBody);
+
+ if (!isset($data) || !is_array($data)) {
+ throw new TokenResponseException('Unable to parse response.');
+ }
+
+ $error = $this->service->getAccessTokenResponseError($data);
+ if (isset($error)) {
+ throw new TokenResponseException('Error in retrieving token: "' . $error . '"');
+ }
+
+ $token = new StdOAuth2Token();
+ $names = $this->service->getAccessTokenArgumentNames();
+
+ $token->setAccessToken($data[$names['access_token']]);
+ unset($data[$names['access_token']]);
+
+ if (isset($data[$names['expires_in']])) {
+ $token->setLifeTime($data[$names['expires_in']]);
+ unset($data[$names['expires_in']]);
+ } else {
+ $token->setLifetime($this->service->getTokenDefaultLifetime());
+ }
+
+ if (isset($data[$names['refresh_token']])) {
+ $token->setRefreshToken($data[$names['refresh_token']]);
+ unset($data[$names['refresh_token']]);
+ }
+
+ $token->setExtraParams($data);
+
+ return $token;
+ }
+
+ /**
+ * Return any additional headers always needed for this service implementation's OAuth calls.
+ *
+ * @return array
+ */
+ protected function getExtraOAuthHeaders()
+ {
+ return $this->service->getExtraOAuthHeaders();
+ }
+
+ /**
+ * Return any additional headers always needed for this service implementation's API calls.
+ *
+ * @return array
+ */
+ protected function getExtraApiHeaders()
+ {
+ return $this->service->getExtraApiHeaders();
+ }
+
+ /**
+ * Returns a class constant from ServiceInterface defining the authorization method used for the API
+ * Header is the sane default.
+ *
+ * @return int
+ */
+ protected function getAuthorizationMethod()
+ {
+ return $this->service->getAuthorizationMethod();
+ }
+
+ /**
+ * Returns the url to redirect to for authorization purposes.
+ *
+ * @param array $additionalParameters
+ * @return Uri
+ */
+ public function getAuthorizationUri(array $additionalParameters = [])
+ {
+ $parameters = array_merge($additionalParameters, [
+ 'type' => 'web_server',
+ 'client_id' => $this->credentials->getConsumerId(),
+ 'redirect_uri' => $this->credentials->getCallbackUrl(),
+ 'response_type' => 'code',
+ ]);
+
+ $parameters['scope'] = implode($this->service->getScopeSeparator(), $this->scopes);
+
+ if ($this->needsStateParameterInAuthUrl()) {
+ if (!isset($parameters['state'])) {
+ $parameters['state'] = $this->generateAuthorizationState();
+ }
+ $this->storeAuthorizationState($parameters['state']);
+ }
+
+ // Build the url
+ $url = clone $this->getAuthorizationEndpoint();
+ foreach ($parameters as $key => $val) {
+ $url->addToQuery($key, $val);
+ }
+
+ return $url;
+ }
+}
diff --git a/common/components/nodge/eauth/src/openid/ControllerBehavior.php b/common/components/nodge/eauth/src/openid/ControllerBehavior.php
new file mode 100644
index 0000000..c1d51ca
--- /dev/null
+++ b/common/components/nodge/eauth/src/openid/ControllerBehavior.php
@@ -0,0 +1,38 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\openid;
+
+use Yii;
+use yii\base\Action;
+use yii\base\ActionFilter;
+
+/**
+ * @package application.extensions.eauth
+ */
+class ControllerBehavior extends ActionFilter
+{
+ /**
+ * This method is invoked right before an action is to be executed (after all possible filters.)
+ * You may override this method to do last-minute preparation for the action.
+ *
+ * @param Action $action the action to be executed.
+ * @return boolean whether the action should continue to be executed.
+ */
+ public function beforeAction($action)
+ {
+ $request = Yii::$app->getRequest();
+
+ if (in_array($request->getBodyParam('openid_mode', ''), ['id_res', 'cancel'])) {
+ $request->enableCsrfValidation = false;
+ }
+
+ return parent::beforeAction($action);
+ }
+}
diff --git a/common/components/nodge/eauth/src/openid/Service.php b/common/components/nodge/eauth/src/openid/Service.php
new file mode 100644
index 0000000..4e22e6a
--- /dev/null
+++ b/common/components/nodge/eauth/src/openid/Service.php
@@ -0,0 +1,183 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\openid;
+
+use \Yii;
+use \LightOpenID;
+use yii\web\HttpException;
+use nodge\eauth\ServiceBase;
+use nodge\eauth\IAuthService;
+use nodge\eauth\ErrorException;
+
+/**
+ * EOpenIDService is a base class for all OpenID providers.
+ *
+ * @package application.extensions.eauth
+ */
+abstract class Service extends ServiceBase implements IAuthService
+{
+
+ /**
+ * @var string a pattern that represents the part of URL-space for which an OpenID Authentication request is valid.
+ * See the spec for more info: http://openid.net/specs/openid-authentication-2_0.html#realms
+ * Note: a pattern can be without http(s):// part
+ */
+ public $realm;
+
+ /**
+ * @var LightOpenID the openid library instance.
+ */
+ private $auth;
+
+ /**
+ * @var string the OpenID authorization url.
+ */
+ protected $url;
+
+ /**
+ * @var array the OpenID required attributes.
+ */
+ protected $requiredAttributes = [];
+
+ /**
+ * @var array the OpenID optional attributes.
+ */
+ protected $optionalAttributes = [];
+
+
+ /**
+ * Initialize the component.
+ */
+ public function init()
+ {
+ parent::init();
+ $this->auth = new LightOpenID(Yii::$app->getRequest()->getHostInfo());
+ }
+
+ /**
+ * Authenticate the user.
+ *
+ * @return boolean whether user was successfuly authenticated.
+ * @throws ErrorException
+ * @throws HttpException
+ */
+ public function authenticate()
+ {
+ if (!empty($_REQUEST['openid_mode'])) {
+ switch ($_REQUEST['openid_mode']) {
+ case 'id_res':
+ $this->id_res();
+ return true;
+ break;
+
+ case 'cancel':
+ $this->cancel();
+ break;
+
+ default:
+ throw new HttpException(400, Yii::t('yii', 'Your request is invalid.'));
+ break;
+ }
+ } else {
+ $this->request();
+ }
+
+ return false;
+ }
+
+ /**
+ * @throws ErrorException
+ */
+ protected function id_res()
+ {
+ try {
+ if ($this->auth->validate()) {
+ $this->attributes['id'] = $this->auth->identity;
+ $this->loadRequiredAttributes();
+ $this->loadOptionalAttributes();
+ $this->authenticated = true;
+ } else {
+ throw new ErrorException(Yii::t('eauth', 'Unable to complete the authentication because the required data was not received.', ['provider' => $this->getServiceTitle()]));
+ }
+ } catch (\Exception $e) {
+ throw new ErrorException($e->getMessage(), $e->getCode());
+ }
+ }
+
+ /**
+ * @throws ErrorException
+ */
+ protected function loadOptionalAttributes()
+ {
+ $attributes = $this->auth->getAttributes();
+ foreach ($this->optionalAttributes as $key => $attr) {
+ if (isset($attributes[$attr[1]])) {
+ $this->attributes[$key] = $attributes[$attr[1]];
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ protected function loadRequiredAttributes()
+ {
+ $attributes = $this->auth->getAttributes();
+ foreach ($this->requiredAttributes as $key => $attr) {
+ if (isset($attributes[$attr[1]])) {
+ $this->attributes[$key] = $attributes[$attr[1]];
+ } else {
+ throw new ErrorException(Yii::t('eauth', 'Unable to complete the authentication because the required data was not received.', ['provider' => $this->getServiceTitle()]));
+ }
+ }
+ }
+
+ /**
+ * @throws ErrorException
+ */
+ protected function request()
+ {
+ $this->auth->identity = $this->url; //Setting identifier
+
+ $this->auth->required = []; //Try to get info from openid provider
+ foreach ($this->requiredAttributes as $attribute) {
+ $this->auth->required[$attribute[0]] = $attribute[1];
+ }
+ foreach ($this->optionalAttributes as $attribute) {
+ $this->auth->required[$attribute[0]] = $attribute[1];
+ }
+
+ $this->auth->realm = $this->getRealm();
+ $this->auth->returnUrl = Yii::$app->getRequest()->getHostInfo() . Yii::$app->getRequest()->getUrl(); //getting return URL
+
+ try {
+ $url = $this->auth->authUrl();
+ Yii::$app->getResponse()->redirect($url)->send();
+ } catch (\Exception $e) {
+ throw new ErrorException($e->getMessage(), $e->getCode());
+ }
+ }
+
+ /**
+ * @return string
+ */
+ protected function getRealm()
+ {
+ if (isset($this->realm)) {
+ if (!preg_match('#^[a-z]+\://#', $this->realm)) {
+ return 'http' . (Yii::$app->getRequest()->getIsSecureConnection() ? 's' : '') . '://' . $this->realm;
+ } else {
+ return $this->realm;
+ }
+ } else {
+ return Yii::$app->getRequest()->getHostInfo();
+ }
+ }
+}
diff --git a/common/components/nodge/eauth/src/services/FacebookOAuth2Service.php b/common/components/nodge/eauth/src/services/FacebookOAuth2Service.php
new file mode 100644
index 0000000..c2f3577
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/FacebookOAuth2Service.php
@@ -0,0 +1,102 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+namespace common\components\nodge\eauth\src\services;
+use nodge\eauth\oauth2\Service;
+/**
+ * Facebook provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class FacebookOAuth2Service extends Service
+{
+ /**
+ * Full list of scopes may be found here:
+ * https://developers.facebook.com/docs/authentication/permissions/
+ */
+ const SCOPE_EMAIL = 'email';
+ const SCOPE_USER_BIRTHDAY = 'user_birthday';
+ const SCOPE_USER_HOMETOWN = 'user_hometown';
+ const SCOPE_USER_LOCATION = 'user_location';
+ const SCOPE_USER_PHOTOS = 'user_photos';
+ protected $name = 'facebook';
+ protected $title = 'Facebook';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 585, 'height' => 290]];
+ protected $scopes = [];
+ protected $providerOptions = [
+ 'authorize' => 'https://www.facebook.com/dialog/oauth',
+ 'access_token' => 'https://graph.facebook.com/oauth/access_token',
+ ];
+ protected $baseApiUrl = 'https://graph.facebook.com/';
+ protected $errorParam = 'error_code';
+ protected $errorDescriptionParam = 'error_message';
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('me');
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['name'];
+ $this->attributes['url'] = $info['link'];
+ return true;
+ }
+ /**
+ * @return array
+ */
+ public function getAccessTokenArgumentNames()
+ {
+ $names = parent::getAccessTokenArgumentNames();
+ $names['expires_in'] = 'expires';
+ return $names;
+ }
+ /**
+ * @param string $response
+ * @return array
+ */
+ public function parseAccessTokenResponse($response)
+ {
+ // Facebook gives us a query string or json
+ if ($response[0] === '{') {
+ return json_decode($response, true);
+ }
+ else {
+ parse_str($response, $data);
+ return $data;
+ }
+ }
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['error'])) {
+ return [
+ 'code' => $response['error']['code'],
+ 'message' => $response['error']['message'],
+ ];
+ } else {
+ return null;
+ }
+ }
+ /**
+ * @param array $data
+ * @return string|null
+ */
+ public function getAccessTokenResponseError($data)
+ {
+ $error = $this->fetchResponseError($data);
+ if (!$error) {
+ return null;
+ }
+ return $error['code'].': '.$error['message'];
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/GitHubOAuth2Service.php b/common/components/nodge/eauth/src/services/GitHubOAuth2Service.php
new file mode 100644
index 0000000..5126d51
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/GitHubOAuth2Service.php
@@ -0,0 +1,107 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use OAuth\Common\Token\TokenInterface;
+use OAuth\OAuth2\Service\ServiceInterface;
+use nodge\eauth\oauth2\Service;
+
+/**
+ * GitHub provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class GitHubOAuth2Service extends Service
+{
+
+ /**
+ * Defined scopes, see http://developer.github.com/v3/oauth/ for definitions
+ */
+ const SCOPE_USER = 'user';
+ const SCOPE_PUBLIC_REPO = 'public_repo';
+ const SCOPE_REPO = 'repo';
+ const SCOPE_DELETE_REPO = 'delete_repo';
+ const SCOPE_GIST = 'gist';
+
+ protected $name = 'github';
+ protected $title = 'GitHub';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 600, 'height' => 450]];
+
+ protected $scopes = [];
+ protected $providerOptions = [
+ 'authorize' => 'https://github.com/login/oauth/authorize',
+ 'access_token' => 'https://github.com/login/oauth/access_token',
+ ];
+ protected $baseApiUrl = 'https://api.github.com/';
+
+ protected $tokenDefaultLifetime = TokenInterface::EOL_NEVER_EXPIRES;
+ protected $errorAccessDeniedCode = 'user_denied';
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('user');
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['login'];
+ $this->attributes['url'] = $info['html_url'];
+
+ return true;
+ }
+
+ /**
+ * Used to configure response type -- we want JSON from github, default is query string format
+ *
+ * @return array
+ */
+ public function getExtraOAuthHeaders()
+ {
+ return ['Accept' => 'application/json'];
+ }
+
+ /**
+ * Required for GitHub API calls.
+ *
+ * @return array
+ */
+ public function getExtraApiHeaders()
+ {
+ return ['Accept' => 'application/vnd.github.beta+json'];
+ }
+
+ /**
+ * @return int
+ */
+ public function getAuthorizationMethod()
+ {
+ return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING;
+ }
+
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['message'])) {
+ return [
+ 'code' => isset($response['error']) ? $response['code'] : 0,
+ 'message' => $response['message'],
+ ];
+ } else {
+ return null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/GoogleOAuth2Service.php b/common/components/nodge/eauth/src/services/GoogleOAuth2Service.php
new file mode 100644
index 0000000..cf389b5
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/GoogleOAuth2Service.php
@@ -0,0 +1,169 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use nodge\eauth\oauth2\Service;
+
+/**
+ * Google provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class GoogleOAuth2Service extends Service
+{
+
+ /**
+ * Defined scopes - More scopes are listed here:
+ * https://developers.google.com/oauthplayground/
+ */
+
+ // Basic
+ const SCOPE_EMAIL = 'email';
+ const SCOPE_PROFILE = 'profile';
+
+ const SCOPE_USERINFO_EMAIL = 'https://www.googleapis.com/auth/userinfo.email';
+ const SCOPE_USERINFO_PROFILE = 'https://www.googleapis.com/auth/userinfo.profile';
+
+ // Google+
+ const SCOPE_GPLUS_ME = 'https://www.googleapis.com/auth/plus.me';
+ const SCOPE_GPLUS_LOGIN = 'https://www.googleapis.com/auth/plus.login';
+
+ // Google Drive
+ const SCOPE_DOCUMENTSLIST = 'https://docs.google.com/feeds/';
+ const SCOPE_SPREADSHEETS = 'https://spreadsheets.google.com/feeds/';
+ const SCOPE_GOOGLEDRIVE = 'https://www.googleapis.com/auth/drive';
+ const SCOPE_DRIVE_APPS = 'https://www.googleapis.com/auth/drive.appdata';
+ const SCOPE_DRIVE_APPS_READ_ONLY = 'https://www.googleapis.com/auth/drive.apps.readonly';
+ const SCOPE_GOOGLEDRIVE_FILES = 'https://www.googleapis.com/auth/drive.file';
+ const SCOPE_DRIVE_METADATA_READ_ONLY = 'https://www.googleapis.com/auth/drive.metadata.readonly';
+ const SCOPE_DRIVE_READ_ONLY = 'https://www.googleapis.com/auth/drive.readonly';
+ const SCOPE_DRIVE_SCRIPTS = 'https://www.googleapis.com/auth/drive.scripts';
+
+ // Adwords
+ const SCOPE_ADSENSE = 'https://www.googleapis.com/auth/adsense';
+ const SCOPE_ADWORDS = 'https://adwords.google.com/api/adwords/';
+ const SCOPE_GAN = 'https://www.googleapis.com/auth/gan'; // google affiliate network...?
+
+ // Google Analytics
+ const SCOPE_ANALYTICS = 'https://www.googleapis.com/auth/analytics';
+ const SCOPE_ANALYTICS_EDIT = 'https://www.googleapis.com/auth/analytics.edit';
+ const SCOPE_ANALYTICS_MANAGE_USERS = 'https://www.googleapis.com/auth/analytics.manage.users';
+ const SCOPE_ANALYTICS_READ_ONLY = 'https://www.googleapis.com/auth/analytics.readonly';
+
+ // Other services
+ const SCOPE_BOOKS = 'https://www.googleapis.com/auth/books';
+ const SCOPE_BLOGGER = 'https://www.googleapis.com/auth/blogger';
+ const SCOPE_CALENDAR = 'https://www.googleapis.com/auth/calendar';
+ const SCOPE_CONTACT = 'https://www.google.com/m8/feeds/';
+ const SCOPE_CHROMEWEBSTORE = 'https://www.googleapis.com/auth/chromewebstore.readonly';
+ const SCOPE_GMAIL = 'https://mail.google.com/mail/feed/atom';
+ const SCOPE_PICASAWEB = 'https://picasaweb.google.com/data/';
+ const SCOPE_SITES = 'https://sites.google.com/feeds/';
+ const SCOPE_URLSHORTENER = 'https://www.googleapis.com/auth/urlshortener';
+ const SCOPE_WEBMASTERTOOLS = 'https://www.google.com/webmasters/tools/feeds/';
+ const SCOPE_TASKS = 'https://www.googleapis.com/auth/tasks';
+
+ // Cloud services
+ const SCOPE_CLOUDSTORAGE = 'https://www.googleapis.com/auth/devstorage.read_write';
+ const SCOPE_CONTENTFORSHOPPING = 'https://www.googleapis.com/auth/structuredcontent'; // what even is this
+ const SCOPE_USER_PROVISIONING = 'https://apps-apis.google.com/a/feeds/user/';
+ const SCOPE_GROUPS_PROVISIONING = 'https://apps-apis.google.com/a/feeds/groups/';
+ const SCOPE_NICKNAME_PROVISIONING = 'https://apps-apis.google.com/a/feeds/alias/';
+
+ // Old
+ const SCOPE_ORKUT = 'https://www.googleapis.com/auth/orkut';
+ const SCOPE_GOOGLELATITUDE =
+ 'https://www.googleapis.com/auth/latitude.all.best https://www.googleapis.com/auth/latitude.all.city';
+ const SCOPE_OPENID = 'openid';
+
+ // YouTube
+ const SCOPE_YOUTUBE_GDATA = 'https://gdata.youtube.com';
+ const SCOPE_YOUTUBE_ANALYTICS_MONETARY = 'https://www.googleapis.com/auth/yt-analytics-monetary.readonly';
+ const SCOPE_YOUTUBE_ANALYTICS = 'https://www.googleapis.com/auth/yt-analytics.readonly';
+ const SCOPE_YOUTUBE = 'https://www.googleapis.com/auth/youtube';
+ const SCOPE_YOUTUBE_READ_ONLY = 'https://www.googleapis.com/auth/youtube.readonly';
+ const SCOPE_YOUTUBE_UPLOAD = 'https://www.googleapis.com/auth/youtube.upload';
+ const SCOPE_YOUTUBE_PATNER = 'https://www.googleapis.com/auth/youtubepartner';
+ const SCOPE_YOUTUBE_PARTNER_EDIT = 'https://www.googleapis.com/auth/youtubepartner-channel-edit';
+
+ // Google Glass
+ const SCOPE_GLASS_TIMELINE = 'https://www.googleapis.com/auth/glass.timeline';
+ const SCOPE_GLASS_LOCATION = 'https://www.googleapis.com/auth/glass.location';
+
+ protected $name = 'google_oauth';
+ protected $title = 'Google';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 500, 'height' => 450]];
+
+ protected $scopes = [self::SCOPE_USERINFO_PROFILE];
+ protected $providerOptions = [
+ 'authorize' => 'https://accounts.google.com/o/oauth2/auth',
+ 'access_token' => 'https://accounts.google.com/o/oauth2/token',
+ ];
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('https://www.googleapis.com/oauth2/v1/userinfo');
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['name'];
+
+ if (!empty($info['link'])) {
+ $this->attributes['url'] = $info['link'];
+ }
+
+ /*if (!empty($info['gender']))
+ $this->attributes['gender'] = $info['gender'] == 'male' ? 'M' : 'F';
+
+ if (!empty($info['picture']))
+ $this->attributes['photo'] = $info['picture'];
+
+ $info['given_name']; // first name
+ $info['family_name']; // last name
+ $info['birthday']; // format: 0000-00-00
+ $info['locale']; // format: en*/
+ }
+
+ /**
+ * Returns the protected resource.
+ *
+ * @param string $url url to request.
+ * @param array $options HTTP request options. Keys: query, data, referer.
+ * @param boolean $parseResponse Whether to parse response.
+ * @return mixed the response.
+ */
+ public function makeSignedRequest($url, $options = [], $parseResponse = true)
+ {
+ if (!isset($options['query']['alt'])) {
+ $options['query']['alt'] = 'json';
+ }
+ return parent::makeSignedRequest($url, $options, $parseResponse);
+ }
+
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['error'])) {
+ return [
+ 'code' => $response['error']['code'],
+ 'message' => $response['error']['message'],
+ ];
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/InstagramOAuth2Service.php b/common/components/nodge/eauth/src/services/InstagramOAuth2Service.php
new file mode 100644
index 0000000..ce5b2b9
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/InstagramOAuth2Service.php
@@ -0,0 +1,90 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use nodge\eauth\oauth2\Service;
+use OAuth\Common\Token\TokenInterface;
+use OAuth\OAuth2\Service\ServiceInterface;
+
+/**
+ * Instagram provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class InstagramOAuth2Service extends Service
+{
+
+ /**
+ * Defined scopes
+ * @link https://instagram.com/developer/authentication/
+ */
+ const SCOPE_BASIC = 'basic';
+ const SCOPE_COMMENTS = 'comments';
+ const SCOPE_RELATIONSHIPS = 'relationships';
+ const SCOPE_LIKES = 'likes';
+
+ protected $name = 'instagram';
+ protected $title = 'Instagram';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 900, 'height' => 550]];
+ protected $popupDisplayName = false;
+
+ protected $scopes = [self::SCOPE_BASIC];
+ protected $providerOptions = [
+ 'authorize' => 'https://api.instagram.com/oauth/authorize/',
+ 'access_token' => 'https://api.instagram.com/oauth/access_token',
+ ];
+ protected $baseApiUrl = 'https://api.instagram.com/v1/';
+
+ protected $tokenDefaultLifetime = TokenInterface::EOL_NEVER_EXPIRES;
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('users/self');
+ $data = $info['data'];
+
+ $this->attributes = array_merge($this->attributes, [
+ 'id' => $data['id'],
+ 'username' => $data['username'],
+ 'full_name' => $data['full_name'],
+ 'profile_picture' => $data['profile_picture'],
+ 'bio' => $data['bio'],
+ 'website' => $data['website'],
+ 'counts' => $data['counts']
+ ]);
+
+ return true;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAuthorizationMethod()
+ {
+ return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING;
+ }
+
+ /**
+ * Returns the protected resource.
+ *
+ * @param string $url url to request.
+ * @param array $options HTTP request options. Keys: query, data, referer.
+ * @param boolean $parseResponse Whether to parse response.
+ * @return mixed the response.
+ */
+ public function makeSignedRequest($url, $options = [], $parseResponse = true)
+ {
+ $options['query']['format'] = 'json';
+ return parent::makeSignedRequest($url, $options, $parseResponse);
+ }
+
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/LinkedinOAuth1Service.php b/common/components/nodge/eauth/src/services/LinkedinOAuth1Service.php
new file mode 100644
index 0000000..e5edb1f
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/LinkedinOAuth1Service.php
@@ -0,0 +1,73 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use nodge\eauth\oauth1\Service;
+
+/**
+ * LinkedIn provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class LinkedinOAuth1Service extends Service
+{
+
+ protected $name = 'linkedin';
+ protected $title = 'LinkedIn';
+ protected $type = 'OAuth1';
+ protected $jsArguments = ['popup' => ['width' => 900, 'height' => 550]];
+
+ protected $providerOptions = [
+ 'request' => 'https://api.linkedin.com/uas/oauth/requestToken',
+ 'authorize' => 'https://www.linkedin.com/uas/oauth/authenticate', // https://www.linkedin.com/uas/oauth/authorize
+ 'access' => 'https://api.linkedin.com/uas/oauth/accessToken',
+ ];
+ protected $baseApiUrl = 'http://api.linkedin.com/v1/';
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('people/~:(id,first-name,last-name,public-profile-url)', [
+ 'query' => [
+ 'format' => 'json',
+ ],
+ ]);
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['firstName'] . ' ' . $info['lastName'];
+ $this->attributes['url'] = $info['publicProfileUrl'];
+
+ return true;
+ }
+
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['error-code'])) {
+ return [
+ 'code' => $response['error-code'],
+ 'message' => $response['message'],
+ ];
+ } else if (isset($response['errorCode'])) {
+ return [
+ 'code' => $response['errorCode'],
+ 'message' => $response['message'],
+ ];
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/LinkedinOAuth2Service.php b/common/components/nodge/eauth/src/services/LinkedinOAuth2Service.php
new file mode 100644
index 0000000..2150aec
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/LinkedinOAuth2Service.php
@@ -0,0 +1,74 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use OAuth\OAuth2\Service\ServiceInterface;
+use nodge\eauth\oauth2\Service;
+
+/**
+ * LinkedIn provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class LinkedinOAuth2Service extends Service
+{
+
+ /**
+ * Defined scopes
+ *
+ * @link http://developer.linkedin.com/documents/authentication#granting
+ */
+ const SCOPE_R_BASICPROFILE = 'r_basicprofile';
+ const SCOPE_R_FULLPROFILE = 'r_fullprofile';
+ const SCOPE_R_EMAILADDRESS = 'r_emailaddress';
+ const SCOPE_R_NETWORK = 'r_network';
+ const SCOPE_R_CONTACTINFO = 'r_contactinfo';
+ const SCOPE_RW_NUS = 'rw_nus';
+ const SCOPE_RW_GROUPS = 'rw_groups';
+ const SCOPE_W_MESSAGES = 'w_messages';
+
+ protected $name = 'linkedin_oauth2';
+ protected $title = 'LinkedIn';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 900, 'height' => 550]];
+
+ protected $scopes = [self::SCOPE_R_BASICPROFILE];
+ protected $providerOptions = [
+ 'authorize' => 'https://www.linkedin.com/uas/oauth2/authorization',
+ 'access_token' => 'https://www.linkedin.com/uas/oauth2/accessToken',
+ ];
+ protected $baseApiUrl = 'https://api.linkedin.com/v1/';
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('people/~:(id,first-name,last-name,public-profile-url)', [
+ 'query' => [
+ 'format' => 'json',
+ ],
+ ]);
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['firstName'] . ' ' . $info['lastName'];
+ $this->attributes['url'] = $info['publicProfileUrl'];
+ $this->attributes['email'] = $info['emailAddress'];
+ return true;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAuthorizationMethod()
+ {
+ return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING_V2;
+ }
+}
diff --git a/common/components/nodge/eauth/src/services/LiveOAuth2Service.php b/common/components/nodge/eauth/src/services/LiveOAuth2Service.php
new file mode 100644
index 0000000..bdaa44f
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/LiveOAuth2Service.php
@@ -0,0 +1,85 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use OAuth\OAuth2\Service\ServiceInterface;
+use nodge\eauth\oauth2\Service;
+
+/**
+ * Microsoft Live provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class LiveOAuth2Service extends Service
+{
+
+ const SCOPE_BASIC = 'wl.basic';
+ const SCOPE_OFFLINE = 'wl.offline_access';
+ const SCOPE_SIGNIN = 'wl.signin';
+ const SCOPE_BIRTHDAY = 'wl.birthday';
+ const SCOPE_CALENDARS = 'wl.calendars';
+ const SCOPE_CALENDARS_UPDATE = 'wl.calendars_update';
+ const SCOPE_CONTACTS_BIRTHDAY = 'wl.contacts_birthday';
+ const SCOPE_CONTACTS_CREATE = 'wl.contacts_create';
+ const SCOPE_CONTACTS_CALENDARS = 'wl.contacts_calendars';
+ const SCOPE_CONTACTS_PHOTOS = 'wl.contacts_photos';
+ const SCOPE_CONTACTS_SKYDRIVE = 'wl.contacts_skydrive';
+ const SCOPE_EMAILS = 'wl.emails';
+ const sCOPE_EVENTS_CREATE = 'wl.events_create';
+ const SCOPE_MESSENGER = 'wl.messenger';
+ const SCOPE_PHONE_NUMBERS = 'wl.phone_numbers';
+ const SCOPE_PHOTOS = 'wl.photos';
+ const SCOPE_POSTAL_ADDRESSES = 'wl.postal_addresses';
+ const SCOPE_SHARE = 'wl.share';
+ const SCOPE_SKYDRIVE = 'wl.skydrive';
+ const SCOPE_SKYDRIVE_UPDATE = 'wl.skydrive_update';
+ const SCOPE_WORK_PROFILE = 'wl.work_profile';
+ const SCOPE_APPLICATIONS = 'wl.applications';
+ const SCOPE_APPLICATIONS_CREATE = 'wl.applications_create';
+
+ protected $name = 'live';
+ protected $title = 'Live';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 500, 'height' => 600]];
+
+ protected $scopes = [self::SCOPE_BASIC];
+ protected $providerOptions = [
+ 'authorize' => 'https://login.live.com/oauth20_authorize.srf',
+ 'access_token' => 'https://login.live.com/oauth20_token.srf',
+ ];
+ protected $baseApiUrl = 'https://apis.live.net/v5.0/';
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('me');
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['name'];
+ $this->attributes['url'] = 'https://profile.live.com/cid-' . $info['id'] . '/';
+
+ $this->attributes['email'] = $info['emails']['account'];
+ $this->attributes['first_name'] = $info['first_name'];
+ $this->attributes['last_name'] = $info['last_name'];
+ $this->attributes['gender'] = $info['gender'];
+ $this->attributes['locale'] = $info['locale'];
+
+ return true;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAuthorizationMethod()
+ {
+ return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING;
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/MailruOAuth2Service.php b/common/components/nodge/eauth/src/services/MailruOAuth2Service.php
new file mode 100644
index 0000000..4c38e3d
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/MailruOAuth2Service.php
@@ -0,0 +1,98 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use nodge\eauth\oauth2\Service;
+
+/**
+ * Mail.Ru provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class MailruOAuth2Service extends Service
+{
+
+ protected $name = 'mailru';
+ protected $title = 'Mail.ru';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 580, 'height' => 400]];
+
+ protected $scopes = [];
+ protected $providerOptions = [
+ 'authorize' => 'https://connect.mail.ru/oauth/authorize',
+ 'access_token' => 'https://connect.mail.ru/oauth/token',
+ ];
+ protected $baseApiUrl = 'http://www.appsmail.ru/platform/api';
+
+ protected function fetchAttributes()
+ {
+ $tokenData = $this->getAccessTokenData();
+
+ $info = $this->makeSignedRequest('/', [
+ 'query' => [
+ 'uids' => $tokenData['params']['x_mailru_vid'],
+ 'method' => 'users.getInfo',
+ 'app_id' => $this->clientId,
+ ],
+ ]);
+
+ $info = $info[0];
+
+ $this->attributes['id'] = $info['uid'];
+ $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name'];
+ $this->attributes['url'] = $info['link'];
+
+ return true;
+ }
+
+ /**
+ * Returns the protected resource.
+ *
+ * @param string $url url to request.
+ * @param array $options HTTP request options. Keys: query, data, referer.
+ * @param boolean $parseResponse Whether to parse response.
+ * @return mixed the response.
+ */
+ public function makeSignedRequest($url, $options = [], $parseResponse = true)
+ {
+ $token = $this->getAccessTokenData();
+ if (isset($token)) {
+ $options['query']['secure'] = 1;
+ $options['query']['session_key'] = $token['access_token'];
+ $params = '';
+ ksort($options['query']);
+ foreach ($options['query'] as $k => $v) {
+ $params .= $k . '=' . $v;
+ }
+ $options['query']['sig'] = md5($params . $this->clientSecret);
+ }
+ return parent::makeSignedRequest($url, $options, $parseResponse);
+ }
+
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['error'])) {
+ return [
+ 'code' => $response['error']['error_code'],
+ 'message' => $response['error']['error_msg'],
+ ];
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/OdnoklassnikiOAuth2Service.php b/common/components/nodge/eauth/src/services/OdnoklassnikiOAuth2Service.php
new file mode 100644
index 0000000..cdf8d2e
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/OdnoklassnikiOAuth2Service.php
@@ -0,0 +1,121 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use nodge\eauth\oauth2\Service;
+
+/**
+ * Odnoklassniki.Ru provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class OdnoklassnikiOAuth2Service extends Service
+{
+
+ const SCOPE_VALUABLE_ACCESS = 'VALUABLE ACCESS';
+ const SCOPE_SET_STATUS = 'SET STATUS';
+ const SCOPE_PHOTO_CONTENT = 'PHOTO CONTENT';
+
+ protected $name = 'odnoklassniki';
+ protected $title = 'Odnoklassniki';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 680, 'height' => 500]];
+
+ protected $clientPublic;
+ protected $scopes = [];
+ protected $scopeSeparator = ';';
+ protected $providerOptions = [
+ 'authorize' => 'http://www.odnoklassniki.ru/oauth/authorize',
+ 'access_token' => 'http://api.odnoklassniki.ru/oauth/token.do',
+ ];
+ protected $baseApiUrl = 'http://api.odnoklassniki.ru/fb.do';
+
+ protected $tokenDefaultLifetime = 1500; // about 25 minutes
+ protected $validateState = false;
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('', [
+ 'query' => [
+ 'method' => 'users.getCurrentUser',
+ 'format' => 'JSON',
+ 'application_key' => $this->clientPublic,
+ 'client_id' => $this->clientId,
+ ],
+ ]);
+
+ $this->attributes['id'] = $info['uid'];
+ $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name'];
+
+ return true;
+ }
+
+ /**
+ * @return string
+ */
+ public function getClientPublic()
+ {
+ return $this->clientPublic;
+ }
+
+ /**
+ * @param string $clientPublic
+ */
+ public function setClientPublic($clientPublic)
+ {
+ $this->clientPublic = $clientPublic;
+ }
+
+ /**
+ * Returns the protected resource.
+ *
+ * @param string $url url to request.
+ * @param array $options HTTP request options. Keys: query, data, referer.
+ * @param boolean $parseResponse Whether to parse response.
+ * @return mixed the response.
+ */
+ public function makeSignedRequest($url, $options = [], $parseResponse = true)
+ {
+ $token = $this->getAccessTokenData();
+ if (isset($token)) {
+ $params = '';
+ ksort($options['query']);
+ foreach ($options['query'] as $k => $v) {
+ $params .= $k . '=' . $v;
+ }
+ $options['query']['sig'] = md5($params . md5($token['access_token'] . $this->clientSecret));
+ $options['query']['access_token'] = $token['access_token'];
+ }
+ return parent::makeSignedRequest($url, $options, $parseResponse);
+ }
+
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['error_code'])) {
+ return [
+ 'code' => $response['error_code'],
+ 'message' => $response['error_msg'],
+ ];
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/common/components/nodge/eauth/src/services/SteamOpenIDService.php b/common/components/nodge/eauth/src/services/SteamOpenIDService.php
new file mode 100644
index 0000000..03ffbdf
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/SteamOpenIDService.php
@@ -0,0 +1,40 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use nodge\eauth\openid\Service;
+
+/**
+ * Steam provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class SteamOpenIDService extends Service
+{
+
+ protected $name = 'steam';
+ protected $title = 'Steam';
+ protected $type = 'OpenID';
+ protected $jsArguments = ['popup' => ['width' => 990, 'height' => 615]];
+
+ protected $url = 'http://steamcommunity.com/openid/';
+
+ protected function fetchAttributes()
+ {
+ if (isset($this->attributes['id'])) {
+ $urlChunks = explode('/', $this->attributes['id']);
+ if ($count = count($urlChunks)) {
+ $name = $urlChunks[$count - 1];
+ $this->attributes['name'] = $name;
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/TwitterOAuth1Service.php b/common/components/nodge/eauth/src/services/TwitterOAuth1Service.php
new file mode 100644
index 0000000..6d6776b
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/TwitterOAuth1Service.php
@@ -0,0 +1,71 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use OAuth\OAuth1\Token\TokenInterface;
+use nodge\eauth\oauth1\Service;
+
+
+/**
+ * Twitter provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class TwitterOAuth1Service extends Service
+{
+
+ protected $name = 'twitter';
+ protected $title = 'Twitter';
+ protected $type = 'OAuth1';
+ protected $jsArguments = ['popup' => ['width' => 900, 'height' => 550]];
+
+ protected $providerOptions = [
+ 'request' => 'https://api.twitter.com/oauth/request_token',
+ 'authorize' => 'https://api.twitter.com/oauth/authenticate', //https://api.twitter.com/oauth/authorize
+ 'access' => 'https://api.twitter.com/oauth/access_token',
+ ];
+ protected $baseApiUrl = 'https://api.twitter.com/1.1/';
+ protected $tokenDefaultLifetime = TokenInterface::EOL_NEVER_EXPIRES;
+
+ /**
+ * @return bool
+ */
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('account/verify_credentials.json');
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['name'];
+ $this->attributes['url'] = 'http://twitter.com/account/redirect_by_id?id=' . $info['id_str'];
+
+ $this->attributes['username'] = $info['screen_name'];
+ $this->attributes['language'] = $info['lang'];
+ $this->attributes['timezone'] = timezone_name_from_abbr('', $info['utc_offset'], date('I'));
+ $this->attributes['photo'] = $info['profile_image_url'];
+
+ return true;
+ }
+
+ /**
+ * Authenticate the user.
+ *
+ * @return boolean whether user was successfuly authenticated.
+ */
+ public function authenticate()
+ {
+ if (isset($_GET['denied'])) {
+ $this->cancel();
+ }
+
+ return parent::authenticate();
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/VKontakteOAuth2Service.php b/common/components/nodge/eauth/src/services/VKontakteOAuth2Service.php
new file mode 100644
index 0000000..9005ee1
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/VKontakteOAuth2Service.php
@@ -0,0 +1,121 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use nodge\eauth\oauth2\Service;
+use OAuth\OAuth2\Service\ServiceInterface;
+
+/**
+ * VKontakte provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class VKontakteOAuth2Service extends Service
+{
+
+ const SCOPE_FRIENDS = 'friends';
+
+ protected $name = 'vkontakte';
+ protected $title = 'VK.com';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 585, 'height' => 350]];
+
+ protected $scopes = [self::SCOPE_FRIENDS];
+ protected $providerOptions = [
+ 'authorize' => 'http://api.vk.com/oauth/authorize',
+ 'access_token' => 'https://api.vk.com/oauth/access_token',
+ ];
+ protected $baseApiUrl = 'https://api.vk.com/method/';
+
+ protected function fetchAttributes()
+ {
+ $tokenData = $this->getAccessTokenData();
+ $info = $this->makeSignedRequest('users.get.json', [
+ 'query' => [
+ 'uids' => $tokenData['params']['user_id'],
+ 'fields' => '', // uid, first_name and last_name is always available
+ 'fields' => 'nickname, sex, bdate, city, country, timezone, photo, photo_medium, photo_big, photo_rec',
+ ],
+ ]);
+
+ $info = $info['response'][0];
+
+ $this->attributes['id'] = $info['uid'];
+ $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name'];
+ $this->attributes['url'] = 'http://vk.com/id' . $info['uid'];
+
+ if (!empty($info['nickname']))
+ $this->attributes['username'] = $info['nickname'];
+ else
+ $this->attributes['username'] = 'id'.$info['uid'];
+
+ $this->attributes['gender'] = $info['sex'] == 1 ? 'F' : 'M';
+
+ $this->attributes['city'] = $info['city'];
+ $this->attributes['country'] = $info['country'];
+
+ $this->attributes['timezone'] = timezone_name_from_abbr('', $info['timezone']*3600, date('I'));;
+
+ $this->attributes['photo'] = $info['photo'];
+ $this->attributes['photo_medium'] = $info['photo_medium'];
+ $this->attributes['photo_big'] = $info['photo_big'];
+ $this->attributes['photo_rec'] = $info['photo_rec'];
+
+ return true;
+ }
+
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['error'])) {
+ return [
+ 'code' => is_string($response['error']) ? 0 : $response['error']['error_code'],
+// 'message' => is_string($response['error']) ? $response['error'] : $response['error']['error_msg'],
+// 'message' => is_string($response['error']) ? $response['error'] : $response['error']['error_msg'],
+ ];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @param array $data
+ * @return string|null
+ */
+ public function getAccessTokenResponseError($data)
+ {
+ if (!isset($data['error'])) {
+ return null;
+ }
+ $error = $data['error'];
+ if (isset($data['error_description'])) {
+ $error .= ': ' . $data['error_description'];
+ }
+ return $error;
+ }
+
+ /**
+ * Returns a class constant from ServiceInterface defining the authorization method used for the API.
+ *
+ * @return int
+ */
+ public function getAuthorizationMethod()
+ {
+ return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING;
+ }
+
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/YahooOpenIDService.php b/common/components/nodge/eauth/src/services/YahooOpenIDService.php
new file mode 100644
index 0000000..f560c7e
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/YahooOpenIDService.php
@@ -0,0 +1,44 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use nodge\eauth\openid\Service;
+
+/**
+ * Yahoo provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class YahooOpenIDService extends Service
+{
+
+ protected $name = 'yahoo';
+ protected $title = 'Yahoo';
+ protected $type = 'OpenID';
+ protected $jsArguments = ['popup' => ['width' => 880, 'height' => 520]];
+
+ protected $url = 'https://me.yahoo.com';
+ protected $requiredAttributes = [
+ 'name' => ['fullname', 'namePerson'],
+ 'login' => ['nickname', 'namePerson/friendly'],
+ 'email' => ['email', 'contact/email'],
+ ];
+ protected $optionalAttributes = [
+ 'language' => ['language', 'pref/language'],
+ 'gender' => ['gender', 'person/gender'],
+ 'timezone' => ['timezone', 'pref/timezone'],
+ 'image' => ['image', 'media/image/default'],
+ ];
+
+ /*protected function fetchAttributes() {
+ $this->attributes['fullname'] = $this->attributes['name'].' '.$this->attributes['lastname'];
+ return true;
+ }*/
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/YandexOAuth2Service.php b/common/components/nodge/eauth/src/services/YandexOAuth2Service.php
new file mode 100644
index 0000000..cd29b49
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/YandexOAuth2Service.php
@@ -0,0 +1,51 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace common\components\nodge\eauth\src\services;
+
+use OAuth\Common\Token\TokenInterface;
+use nodge\eauth\oauth2\Service;
+
+/**
+ * Yandex OAuth provider class.
+ *
+ * @package application.extensions.eauth.services
+ */
+class YandexOAuth2Service extends Service
+{
+
+ protected $name = 'yandex_oauth';
+ protected $title = 'Yandex';
+ protected $type = 'OAuth2';
+ protected $jsArguments = ['popup' => ['width' => 500, 'height' => 450]];
+ protected $tokenDefaultLifetime = TokenInterface::EOL_NEVER_EXPIRES;
+
+ protected $scope = [];
+ protected $providerOptions = [
+ 'authorize' => 'https://oauth.yandex.ru/authorize',
+ 'access_token' => 'https://oauth.yandex.ru/token',
+ ];
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('https://login.yandex.ru/info');
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['real_name'];
+ //$this->attributes['login'] = $info['display_name'];
+ //$this->attributes['email'] = $info['emails'][0];
+ //$this->attributes['email'] = $info['default_email'];
+ $this->attributes['gender'] = ($info['sex'] == 'male') ? 'M' : 'F';
+
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/extended/FacebookOAuth2Service.php b/common/components/nodge/eauth/src/services/extended/FacebookOAuth2Service.php
new file mode 100644
index 0000000..7d7df44
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/extended/FacebookOAuth2Service.php
@@ -0,0 +1,33 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\services\extended;
+
+class FacebookOAuth2Service extends \nodge\eauth\services\FacebookOAuth2Service
+{
+
+ protected $scopes = [
+ self::SCOPE_EMAIL,
+ self::SCOPE_USER_BIRTHDAY,
+ self::SCOPE_USER_HOMETOWN,
+ self::SCOPE_USER_LOCATION,
+ self::SCOPE_USER_PHOTOS,
+ ];
+
+ /**
+ * http://developers.facebook.com/docs/reference/api/user/
+ *
+ * @see FacebookOAuth2Service::fetchAttributes()
+ */
+ protected function fetchAttributes()
+ {
+ $this->attributes = $this->makeSignedRequest('me');
+ return true;
+ }
+}
diff --git a/common/components/nodge/eauth/src/services/extended/GitHubOAuth2Service.php b/common/components/nodge/eauth/src/services/extended/GitHubOAuth2Service.php
new file mode 100644
index 0000000..7a6397a
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/extended/GitHubOAuth2Service.php
@@ -0,0 +1,31 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\services\extended;
+
+class GitHubOAuth2Service extends \nodge\eauth\services\GitHubOAuth2Service
+{
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('user');
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['login'];
+ $this->attributes['url'] = $info['html_url'];
+
+ $this->attributes['following'] = $info['following'];
+ $this->attributes['followers'] = $info['followers'];
+ $this->attributes['public_repos'] = $info['public_repos'];
+ $this->attributes['public_gists'] = $info['public_gists'];
+ $this->attributes['avatar_url'] = $info['avatar_url'];
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/extended/MailruOAuth2Service.php b/common/components/nodge/eauth/src/services/extended/MailruOAuth2Service.php
new file mode 100644
index 0000000..188af2d
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/extended/MailruOAuth2Service.php
@@ -0,0 +1,39 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\services\extended;
+
+class MailruOAuth2Service extends \nodge\eauth\services\MailruOAuth2Service
+{
+
+ protected function fetchAttributes()
+ {
+ $tokenData = $this->getAccessTokenData();
+
+ $info = $this->makeSignedRequest('/', [
+ 'query' => [
+ 'uids' => $tokenData['params']['x_mailru_vid'],
+ 'method' => 'users.getInfo',
+ 'app_id' => $this->clientId,
+ ],
+ ]);
+
+ $info = $info[0];
+
+ $this->attributes['id'] = $info['uid'];
+ $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name'];
+ $this->attributes['first_name'] = $info['first_name'];
+ $this->attributes['last_name'] = $info['last_name'];
+ $this->attributes['url'] = $info['link'];
+ $this->attributes['photo'] = $info['pic'];
+
+ return true;
+ }
+
+}
diff --git a/common/components/nodge/eauth/src/services/extended/OdnoklassnikiOAuth2Service.php b/common/components/nodge/eauth/src/services/extended/OdnoklassnikiOAuth2Service.php
new file mode 100644
index 0000000..2e98055
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/extended/OdnoklassnikiOAuth2Service.php
@@ -0,0 +1,58 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\services\extended;
+
+class OdnoklassnikiOAuth2Service extends \nodge\eauth\services\OdnoklassnikiOAuth2Service
+{
+
+ protected $scopes = [self::SCOPE_VALUABLE_ACCESS];
+
+ protected function fetchAttributes()
+ {
+ parent::fetchAttributes();
+
+ $info = $this->makeSignedRequest('', [
+ 'query' => [
+ 'method' => 'users.getInfo',
+ 'uids' => $this->attributes['id'],
+ 'fields' => 'url_profile',
+ 'format' => 'JSON',
+ 'application_key' => $this->clientPublic,
+ 'client_id' => $this->clientId,
+ ],
+ ]);
+
+ preg_match('/\d+\/{0,1}$/', $info[0]->url_profile, $matches);
+ $this->attributes['id'] = (int)$matches[0];
+ $this->attributes['url'] = $info[0]->url_profile;
+
+ return true;
+ }
+
+
+ /**
+ * @param string $link
+ * @param string $message
+ * @return array
+ */
+ public function wallPost($link, $message)
+ {
+ return $this->makeSignedRequest('', [
+ 'query' => [
+ 'application_key' => $this->clientPublic,
+ 'method' => 'share.addLink',
+ 'format' => 'JSON',
+ 'linkUrl' => $link,
+ 'comment' => $message,
+ ],
+ ]);
+ }
+
+}
diff --git a/common/components/nodge/eauth/src/services/extended/TwitterOAuth1Service.php b/common/components/nodge/eauth/src/services/extended/TwitterOAuth1Service.php
new file mode 100644
index 0000000..e59963d
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/extended/TwitterOAuth1Service.php
@@ -0,0 +1,48 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\services\extended;
+
+class TwitterOAuth1Service extends \nodge\eauth\services\TwitterOAuth1Service
+{
+
+ protected function fetchAttributes()
+ {
+ $info = $this->makeSignedRequest('account/verify_credentials.json');
+
+ $this->attributes['id'] = $info['id'];
+ $this->attributes['name'] = $info['name'];
+ $this->attributes['url'] = 'http://twitter.com/account/redirect_by_id?id=' . $info['id_str'];
+
+ $this->attributes['username'] = $info['screen_name'];
+ $this->attributes['language'] = $info['lang'];
+ $this->attributes['timezone'] = timezone_name_from_abbr('', $info['utc_offset'], date('I'));
+ $this->attributes['photo'] = $info['profile_image_url'];
+
+ return true;
+ }
+
+ /**
+ * Returns the error array.
+ *
+ * @param array $response
+ * @return array the error array with 2 keys: code and message. Should be null if no errors.
+ */
+ protected function fetchResponseError($response)
+ {
+ if (isset($response['errors'])) {
+ $first = reset($response['errors']);
+ return [
+ 'code' => $first['code'],
+ 'message' => $first['message'],
+ ];
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/services/extended/VKontakteOAuth2Service.php b/common/components/nodge/eauth/src/services/extended/VKontakteOAuth2Service.php
new file mode 100644
index 0000000..f2cfe3b
--- /dev/null
+++ b/common/components/nodge/eauth/src/services/extended/VKontakteOAuth2Service.php
@@ -0,0 +1,49 @@
+
+ * @link http://github.com/Nodge/yii2-eauth/
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ */
+
+namespace nodge\eauth\services\extended;
+
+class VKontakteOAuth2Service extends \nodge\eauth\services\VKontakteOAuth2Service
+{
+
+ // protected $scope = 'friends';
+
+ protected function fetchAttributes()
+ {
+ $tokenData = $this->getAccessTokenData();
+ $info = $this->makeSignedRequest('users.get.json', [
+ 'query' => [
+ 'uids' => $tokenData['params']['user_id'],
+ 'fields' => '', // uid, first_name and last_name is always available
+ 'fields' => 'nickname, sex, bdate, city, country, timezone, photo, photo_medium, photo_big, photo_rec',
+ ],
+ ]);
+
+ $info = $info['response'][0];
+
+ $this->attributes = $info;
+ $this->attributes['id'] = $info['uid'];
+ $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name'];
+ $this->attributes['url'] = 'http://vk.com/id' . $info['uid'];
+
+ if (!empty($info['nickname'])) {
+ $this->attributes['username'] = $info['nickname'];
+ } else {
+ $this->attributes['username'] = 'id' . $info['uid'];
+ }
+
+ $this->attributes['gender'] = $info['sex'] == 1 ? 'F' : 'M';
+
+ if (!empty($info['timezone'])) {
+ $this->attributes['timezone'] = timezone_name_from_abbr('', $info['timezone'] * 3600, date('I'));
+ }
+
+ return true;
+ }
+}
diff --git a/common/components/nodge/eauth/src/views/redirect.php b/common/components/nodge/eauth/src/views/redirect.php
new file mode 100644
index 0000000..621c5c3
--- /dev/null
+++ b/common/components/nodge/eauth/src/views/redirect.php
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/components/nodge/eauth/src/views/widget.php b/common/components/nodge/eauth/src/views/widget.php
new file mode 100644
index 0000000..e4caf06
--- /dev/null
+++ b/common/components/nodge/eauth/src/views/widget.php
@@ -0,0 +1,38 @@
+ $assetBundle])->register($this);
+
+// Open the authorization dilalog in popup window.
+if ($popup) {
+ $options = [];
+ foreach ($services as $name => $service) {
+ $options[$service->id] = $service->jsArguments;
+ }
+ $this->registerJs('$("#' . $id . '").eauth(' . json_encode($options) . ');');
+}
+
+?>
+
+
+ $service) {
+ echo '';
+ echo Html::a($service->title, [$action, 'service' => $name], [
+ 'class' => 'eauth-service-link',
+ 'data-eauth-service' => $service->id,
+ ]);
+ echo ' ';
+ }
+ ?>
+
+
diff --git a/common/components/nodge/lightopenid/CHANGELOG.md b/common/components/nodge/lightopenid/CHANGELOG.md
new file mode 100644
index 0000000..2509481
--- /dev/null
+++ b/common/components/nodge/lightopenid/CHANGELOG.md
@@ -0,0 +1,24 @@
+# LightOpenID Change Log
+
+
+## v1.1.2 (January 15, 2013)
+
+`fix` Fixed a bug in the proxy configuration.
+
+
+## v1.1.1 (December 21, 2012)
+
+`add` Added support for overriding the initial URL XRDS lookup.
+
+
+## v1.1.0 (December 02, 2012)
+
+`add` Added support for connecting through a proxy.
+`add` Added support for an OpenID+OAuth hybrid protocol.
+`add` Added SSL-validation support for HEAD-requests.
+`fix` Fixed a bug in the attribute exchange implementation.
+`fix` Fixed a bug in stream defaults after a HEAD request.
+
+
+## v1.0.0 (June 08, 2012)
+`fix` Fixed a bug causing validation failure when using streams.
diff --git a/common/components/nodge/lightopenid/LICENSE b/common/components/nodge/lightopenid/LICENSE
new file mode 100644
index 0000000..3bd2522
--- /dev/null
+++ b/common/components/nodge/lightopenid/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010, Mewp
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/common/components/nodge/lightopenid/README.md b/common/components/nodge/lightopenid/README.md
new file mode 100644
index 0000000..2b356b0
--- /dev/null
+++ b/common/components/nodge/lightopenid/README.md
@@ -0,0 +1,94 @@
+# LightOpenID
+
+Lightweight PHP5 library for easy OpenID authentication.
+
+* `Version....:` [**1.1.2** :arrow_double_down:][1]
+ ( *see [the change log][2] for details* )
+* `Released on:` January 15, 2013
+* `Source code:` [Gitorious :link:][3]
+
+ [GitHub :octocat:][4]
+* `Homepage...:` http://code.google.com/p/lightopenid/
+* `Author.....:` Mewp (http://mewp.s4w.pl/)
+
+[1]: https://github.com/iignatov/LightOpenID/archive/master.zip
+[2]: http://github.com/iignatov/LightOpenID/blob/master/CHANGELOG.md
+[3]: http://gitorious.org/lightopenid
+[4]: http://github.com/iignatov/LightOpenID
+
+
+## Quick start
+
+### Sign-on with OpenID in just 2 steps:
+
+ 1. Authentication with the provider:
+
+ ```php
+ $openid = new LightOpenID('my-host.example.org');
+
+ $openid->identity = 'ID supplied by user';
+
+ header('Location: ' . $openid->authUrl());
+ ```
+ 2. Verification:
+
+ ```php
+ $openid = new LightOpenID('my-host.example.org');
+
+ if ($openid->mode) {
+ echo $openid->validate() ? 'Logged in.' : 'Failed!';
+ }
+ ```
+
+### Support for AX and SREG extensions:
+
+ To use the AX and SREG extensions, specify `$openid->required` and/or `$openid->optional`
+ before calling `$openid->authUrl()`. These are arrays, with values being AX schema paths
+ (the 'path' part of the URL). For example:
+
+ ```php
+ $openid->required = array('namePerson/friendly', 'contact/email');
+ $openid->optional = array('namePerson/first');
+ ```
+
+ Note that if the server supports only SREG or OpenID 1.1, these are automaticaly mapped
+ to SREG names. To get the values use:
+
+ ```php
+ $openid->getAttributes();
+ ```
+
+ For more information see [USAGE.md](http://github.com/iignatov/LightOpenID/blob/master/USAGE.md).
+
+
+## Requirements
+
+This library requires PHP >= 5.1.2 with cURL or HTTP/HTTPS stream wrappers enabled.
+
+
+## Features
+
+* Easy to use - you can code a functional client in less than ten lines of code.
+* Uses cURL if avaiable, PHP-streams otherwise.
+* Supports both OpenID 1.1 and 2.0.
+* Supports Yadis discovery.
+* Supports only stateless/dumb protocol.
+* Works with PHP >= 5.
+* Generates no errors with `error_reporting(E_ALL | E_STRICT)`.
+
+
+## Links
+
+* [JavaScript OpenID Selector](http://code.google.com/p/openid-selector/) -
+ simple user interface that can be used with LightOpenID.
+* [HybridAuth](http://hybridauth.sourceforge.net/) -
+ easy to install and use social sign on PHP library, which uses LightOpenID.
+* [OpenID Dev Specifications](http://openid.net/developers/specs/) -
+ documentation for the OpenID extensions and related topics.
+
+
+## License
+
+[LightOpenID](http://github.com/iignatov/LightOpenID)
+is an open source software available under the
+[MIT License](http://opensource.org/licenses/mit-license.php).
diff --git a/common/components/nodge/lightopenid/USAGE.md b/common/components/nodge/lightopenid/USAGE.md
new file mode 100644
index 0000000..44800e1
--- /dev/null
+++ b/common/components/nodge/lightopenid/USAGE.md
@@ -0,0 +1,144 @@
+# LightOpenID Quick Start
+
+
+## Sign-on with OpenID is a two step process:
+
+ 1. Step one is authentication with the provider:
+
+ ```php
+ $openid = new LightOpenID('my-host.example.org');
+
+ $openid->identity = 'ID supplied by the user';
+
+ header('Location: ' . $openid->authUrl());
+ ```
+
+ The provider then sends various parameters via GET, one of which is `openid_mode`.
+
+ 2. Step two is verification:
+
+ ```php
+ $openid = new LightOpenID('my-host.example.org');
+
+ if ($openid->mode) {
+ echo $openid->validate() ? 'Logged in.' : 'Failed!';
+ }
+ ```
+
+
+### Notes:
+
+ Change 'my-host.example.org' to your domain name. Do NOT use `$_SERVER['HTTP_HOST']`
+ for that, unless you know what you're doing.
+
+ Optionally, you can set `$returnUrl` and `$realm` (or `$trustRoot`, which is an alias).
+ The default values for those are:
+
+ ```php
+ $openid->realm = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
+ $openid->returnUrl = $openid->realm . $_SERVER['REQUEST_URI'];
+ ```
+
+ If you don't know their meaning, refer to any OpenID tutorial, or specification.
+
+
+## Basic configuration options:
+
+
+
+ name
+ description
+
+
+ identity
+
+ Sets (or gets) the identity supplied by an user. Set it
+ before calling authUrl(), and get after validate().
+
+
+
+ returnUrl
+
+ Users will be redirected to this url after they complete
+ authentication with their provider. Default: current url.
+
+
+
+ realm
+
+ The realm user is signing into. Providers usually say
+ "You are sgning into $realm". Must be in the same domain
+ as returnUrl. Usually, this should be the host part of
+ your site's url. And that's the default.
+
+
+
+ required and optional
+
+ Attempts to fetch more information about an user.
+ See Common AX attributes .
+
+
+
+ verify_peer
+
+ When using https, attempts to verify peer's certificate.
+ See CURLOPT_SSL_VERIFYPEER .
+
+
+
+ cainfo and capath
+
+ When verify_peer is true, sets the CA info file and directory.
+ See CURLOPT_SSL_CAINFO
+ and CURLOPT_SSL_CAPATH .
+
+
+
+
+
+## AX and SREG extensions are supported:
+
+ To use them, specify `$openid->required` and/or `$openid->optional` before calling
+ `$openid->authUrl()`. These are arrays, with values being AX schema paths (the 'path'
+ part of the URL). For example:
+
+ ```php
+ $openid->required = array('namePerson/friendly', 'contact/email');
+ $openid->optional = array('namePerson/first');
+ ```
+
+ Note that if the server supports only SREG or OpenID 1.1, these are automaticaly mapped
+ to SREG names, so that user doesn't have to know anything about the server.
+ To get the values, use `$openid->getAttributes()`.
+
+
+### Common AX attributes
+
+ Here is a list of the more common AX attributes (from [axschema.org](http://www.axschema.org/types/)):
+
+ Name | Meaning
+ ------------------------|---------------
+ namePerson/friendly | Alias/Username
+ contact/email | Email
+ namePerson | Full name
+ birthDate | Birth date
+ person/gender | Gender
+ contact/postalCode/home | Postal code
+ contact/country/home | Country
+ pref/language | Language
+ pref/timezone | Time zone
+
+ Note that even if you mark some field as required, there is no guarantee that you'll get any
+ information from a provider. Not all providers support all of these attributes, and some don't
+ support these extensions at all.
+
+ Google, for example, completely ignores optional parameters, and for the required ones, it supports,
+ according to [it's website](http://code.google.com/apis/accounts/docs/OpenID.html):
+
+ * namePerson/first (first name)
+ * namePerson/last (last name)
+ * contact/country/home
+ * contact/email
+ * pref/language
+
diff --git a/common/components/nodge/lightopenid/composer.json b/common/components/nodge/lightopenid/composer.json
new file mode 100644
index 0000000..e3bfdf8
--- /dev/null
+++ b/common/components/nodge/lightopenid/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "nodge/lightopenid",
+ "description": "Lightweight PHP5 library for easy OpenID authentication.",
+ "keywords": ["openid", "authentication"],
+ "homepage": "https://github.com/Nodge/LightOpenID",
+ "type": "library",
+ "license": "MIT License",
+ "authors": [
+ {
+ "name": "Mewp",
+ "homepage": "http://code.google.com/p/lightopenid/"
+ },
+ {
+ "name": "Ignat Ignatov",
+ "homepage": "https://github.com/iignatov/LightOpenID"
+ }
+ ],
+ "require": {
+ "php": ">=5.2"
+ },
+ "autoload": {
+ "classmap": ["openid.php", "provider/provider.php"]
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/lightopenid/example-google.php b/common/components/nodge/lightopenid/example-google.php
new file mode 100644
index 0000000..ade34d3
--- /dev/null
+++ b/common/components/nodge/lightopenid/example-google.php
@@ -0,0 +1,28 @@
+mode) {
+ if (isset($_GET['login'])) {
+ $openid->identity = 'https://www.google.com/accounts/o8/id';
+ header('Location: ' . $openid->authUrl());
+ }
+?>
+
+mode == 'cancel') {
+ echo 'User has canceled authentication!';
+ } else {
+ echo 'User ' . ($openid->validate() ? $openid->identity . ' has ' : 'has not ') . 'logged in.';
+ }
+} catch(ErrorException $e) {
+ echo $e->getMessage();
+}
diff --git a/common/components/nodge/lightopenid/example-google_apps.php b/common/components/nodge/lightopenid/example-google_apps.php
new file mode 100644
index 0000000..9ccbd23
--- /dev/null
+++ b/common/components/nodge/lightopenid/example-google_apps.php
@@ -0,0 +1,32 @@
+xrdsOverride = array(
+ '#^http://' . $domain . '/openid\?id=\d+$#',
+ 'https://www.google.com/accounts/o8/site-xrds?hd=' . $domain
+ );
+
+ if (!$openid->mode) {
+ if (isset($_GET['login'])) {
+ $openid->identity = 'https://www.google.com/accounts/o8/site-xrds?hd=' . $domain;
+ header('Location: ' . $openid->authUrl());
+ }
+?>
+
+mode == 'cancel') {
+ echo 'User has canceled authentication!';
+ } else {
+ echo 'User ' . ($openid->validate() ? $openid->identity . ' has ' : 'has not ') . 'logged in.';
+ }
+} catch(ErrorException $e) {
+ echo $e->getMessage();
+}
diff --git a/common/components/nodge/lightopenid/example.php b/common/components/nodge/lightopenid/example.php
new file mode 100644
index 0000000..342c5ff
--- /dev/null
+++ b/common/components/nodge/lightopenid/example.php
@@ -0,0 +1,28 @@
+mode) {
+ if(isset($_POST['openid_identifier'])) {
+ $openid->identity = $_POST['openid_identifier'];
+ # The following two lines request email, full name, and a nickname
+ # from the provider. Remove them if you don't need that data.
+ $openid->required = array('contact/email');
+ $openid->optional = array('namePerson', 'namePerson/friendly');
+ header('Location: ' . $openid->authUrl());
+ }
+?>
+
+mode == 'cancel') {
+ echo 'User has canceled authentication!';
+ } else {
+ echo 'User ' . ($openid->validate() ? $openid->identity . ' has ' : 'has not ') . 'logged in.';
+ print_r($openid->getAttributes());
+ }
+} catch(ErrorException $e) {
+ echo $e->getMessage();
+}
diff --git a/common/components/nodge/lightopenid/openid.php b/common/components/nodge/lightopenid/openid.php
new file mode 100644
index 0000000..29c93f8
--- /dev/null
+++ b/common/components/nodge/lightopenid/openid.php
@@ -0,0 +1,979 @@
+= 5.1.2 with cURL or HTTP/HTTPS stream wrappers enabled.
+ *
+ * @version v1.1.2 2013-01-15
+ * @link http://gitorious.org/lightopenid Official Repo
+ * @link http://github.com/iignatov/LightOpenID GitHub Clone
+ * @author Mewp
+ * @copyright Copyright (c) 2010, Mewp
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+class LightOpenID
+{
+ public $returnUrl
+ , $required = array()
+ , $optional = array()
+ , $verify_peer = null
+ , $capath = null
+ , $cainfo = null
+ , $data
+ , $oauth = array();
+ private $identity, $claimed_id;
+ protected $server, $version, $trustRoot, $aliases, $identifier_select = false
+ , $ax = false, $sreg = false, $setup_url = null, $headers = array(), $proxy = null
+ , $xrds_override_pattern = null, $xrds_override_replacement = null;
+ static protected $ax_to_sreg = array(
+ 'namePerson/friendly' => 'nickname',
+ 'contact/email' => 'email',
+ 'namePerson' => 'fullname',
+ 'birthDate' => 'dob',
+ 'person/gender' => 'gender',
+ 'contact/postalCode/home' => 'postcode',
+ 'contact/country/home' => 'country',
+ 'pref/language' => 'language',
+ 'pref/timezone' => 'timezone',
+ );
+
+ function __construct($host, $proxy = null)
+ {
+ $this->trustRoot = (strpos($host, '://') ? $host : 'http://' . $host);
+ if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
+ || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
+ && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
+ ) {
+ $this->trustRoot = (strpos($host, '://') ? $host : 'https://' . $host);
+ }
+
+ if(($host_end = strpos($this->trustRoot, '/', 8)) !== false) {
+ $this->trustRoot = substr($this->trustRoot, 0, $host_end);
+ }
+
+ $this->set_proxy($proxy);
+
+ $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?');
+ $this->returnUrl = $this->trustRoot . $uri;
+
+ $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
+
+ if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) {
+ throw new ErrorException('You must have either https wrappers or curl enabled.');
+ }
+ }
+
+ function __set($name, $value)
+ {
+ switch ($name) {
+ case 'identity':
+ if (strlen($value = trim((String) $value))) {
+ if (preg_match('#^xri:/*#i', $value, $m)) {
+ $value = substr($value, strlen($m[0]));
+ } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) {
+ $value = "http://$value";
+ }
+ if (preg_match('#^https?://[^/]+$#i', $value, $m)) {
+ $value .= '/';
+ }
+ }
+ $this->$name = $this->claimed_id = $value;
+ break;
+ case 'trustRoot':
+ case 'realm':
+ $this->trustRoot = trim($value);
+ break;
+ case 'xrdsOverride':
+ if (is_array($value)) {
+ list($pattern, $replacement) = $value;
+ $this->xrds_override_pattern = $pattern;
+ $this->xrds_override_replacement = $replacement;
+ } else {
+ trigger_error('Invalid value specified for "xrdsOverride".', E_USER_ERROR);
+ }
+ break;
+ }
+ }
+
+ function __get($name)
+ {
+ switch ($name) {
+ case 'identity':
+ # We return claimed_id instead of identity,
+ # because the developer should see the claimed identifier,
+ # i.e. what he set as identity, not the op-local identifier (which is what we verify)
+ return $this->claimed_id;
+ case 'trustRoot':
+ case 'realm':
+ return $this->trustRoot;
+ case 'mode':
+ return empty($this->data['openid_mode']) ? null : $this->data['openid_mode'];
+ }
+ }
+
+ function set_proxy($proxy)
+ {
+ if (!empty($proxy)) {
+ // When the proxy is a string - try to parse it.
+ if (!is_array($proxy)) {
+ $proxy = parse_url($proxy);
+ }
+
+ // Check if $proxy is valid after the parsing.
+ if ($proxy && !empty($proxy['host'])) {
+ // Make sure that a valid port number is specified.
+ if (array_key_exists('port', $proxy)) {
+ if (!is_int($proxy['port'])) {
+ $proxy['port'] = is_numeric($proxy['port']) ? intval($proxy['port']) : 0;
+ }
+
+ if ($proxy['port'] <= 0) {
+ throw new ErrorException('The specified proxy port number is invalid.');
+ }
+ }
+
+ $this->proxy = $proxy;
+ }
+ }
+ }
+
+ /**
+ * Checks if the server specified in the url exists.
+ *
+ * @param $url url to check
+ * @return true, if the server exists; false otherwise
+ */
+ function hostExists($url)
+ {
+ if (strpos($url, '/') === false) {
+ $server = $url;
+ } else {
+ $server = @parse_url($url, PHP_URL_HOST);
+ }
+
+ if (!$server) {
+ return false;
+ }
+
+ return !!gethostbynamel($server);
+ }
+
+ protected function request_curl($url, $method='GET', $params=array(), $update_claimed_id)
+ {
+ $params = http_build_query($params, '', '&');
+ $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : ''));
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*'));
+
+ if (!empty($this->proxy)) {
+ curl_setopt($curl, CURLOPT_PROXY, $this->proxy['host']);
+
+ if (!empty($this->proxy['port'])) {
+ curl_setopt($curl, CURLOPT_PROXYPORT, $this->proxy['port']);
+ }
+
+ if (!empty($this->proxy['user'])) {
+ curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);
+ }
+ }
+
+ if($this->verify_peer !== null) {
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
+ if($this->capath) {
+ curl_setopt($curl, CURLOPT_CAPATH, $this->capath);
+ }
+
+ if($this->cainfo) {
+ curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
+ }
+ }
+
+ if ($method == 'POST') {
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
+ } elseif ($method == 'HEAD') {
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_NOBODY, true);
+ } else {
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ }
+ $response = curl_exec($curl);
+
+ if($method == 'HEAD' && curl_getinfo($curl, CURLINFO_HTTP_CODE) == 405) {
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ $response = curl_exec($curl);
+ $response = substr($response, 0, strpos($response, "\r\n\r\n"));
+ }
+
+ if($method == 'HEAD' || $method == 'GET') {
+ $header_response = $response;
+
+ # If it's a GET request, we want to only parse the header part.
+ if($method == 'GET') {
+ $header_response = substr($response, 0, strpos($response, "\r\n\r\n"));
+ }
+
+ $headers = array();
+ foreach(explode("\n", $header_response) as $header) {
+ $pos = strpos($header,':');
+ if ($pos !== false) {
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+ }
+ }
+
+ if($update_claimed_id) {
+ # Updating claimed_id in case of redirections.
+ $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
+ if($effective_url != $url) {
+ $this->identity = $this->claimed_id = $effective_url;
+ }
+ }
+
+ if($method == 'HEAD') {
+ return $headers;
+ } else {
+ $this->headers = $headers;
+ }
+ }
+
+ if (curl_errno($curl)) {
+ throw new ErrorException(curl_error($curl), curl_errno($curl));
+ }
+
+ return $response;
+ }
+
+ protected function parse_header_array($array, $update_claimed_id)
+ {
+ $headers = array();
+ foreach($array as $header) {
+ $pos = strpos($header,':');
+ if ($pos !== false) {
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+
+ # Following possible redirections. The point is just to have
+ # claimed_id change with them, because the redirections
+ # are followed automatically.
+ # We ignore redirections with relative paths.
+ # If any known provider uses them, file a bug report.
+ if($name == 'location' && $update_claimed_id) {
+ if(strpos($headers[$name], 'http') === 0) {
+ $this->identity = $this->claimed_id = $headers[$name];
+ } elseif($headers[$name][0] == '/') {
+ $parsed_url = parse_url($this->claimed_id);
+ $this->identity =
+ $this->claimed_id = $parsed_url['scheme'] . '://'
+ . $parsed_url['host']
+ . $headers[$name];
+ }
+ }
+ }
+ }
+ return $headers;
+ }
+
+ protected function request_streams($url, $method='GET', $params=array(), $update_claimed_id)
+ {
+ if(!$this->hostExists($url)) {
+ throw new ErrorException("Could not connect to $url.", 404);
+ }
+
+ $params = http_build_query($params, '', '&');
+ switch($method) {
+ case 'GET':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'ignore_errors' => true,
+ ), 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST),
+ ),
+ );
+ $url = $url . ($params ? '?' . $params : '');
+ if (!empty($this->proxy)) {
+ $opts['http']['proxy'] = $this->proxy_url();
+ }
+ break;
+ case 'POST':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'header' => 'Content-type: application/x-www-form-urlencoded',
+ 'content' => $params,
+ 'ignore_errors' => true,
+ ), 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST),
+ ),
+ );
+ if (!empty($this->proxy)) {
+ $opts['http']['proxy'] = $this->proxy_url();
+ }
+ break;
+ case 'HEAD':
+ // We want to send a HEAD request, but since get_headers() doesn't
+ // accept $context parameter, we have to change the defaults.
+ $default = stream_context_get_options(stream_context_get_default());
+
+ // PHP does not reset all options. Instead, it just sets the options
+ // available in the passed array, therefore set the defaults manually.
+ $default += array(
+ 'http' => array(),
+ 'ssl' => array()
+ );
+ $default['http'] += array(
+ 'method' => 'GET',
+ 'header' => '',
+ 'ignore_errors' => false
+ );
+ $default['ssl'] += array(
+ 'CN_match' => ''
+ );
+
+ $opts = array(
+ 'http' => array(
+ 'method' => 'HEAD',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'ignore_errors' => true,
+ ),
+ 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST)
+ )
+ );
+
+ // Enable validation of the SSL certificates.
+ if ($this->verify_peer) {
+ $default['ssl'] += array(
+ 'verify_peer' => false,
+ 'capath' => '',
+ 'cafile' => ''
+ );
+ $opts['ssl'] += array(
+ 'verify_peer' => true,
+ 'capath' => $this->capath,
+ 'cafile' => $this->cainfo
+ );
+ }
+
+ // Change the stream context options.
+ stream_context_get_default($opts);
+
+ $headers = get_headers($url . ($params ? '?' . $params : ''));
+
+ // Restore the stream context options.
+ stream_context_get_default($default);
+
+ if (!empty($headers)) {
+ if (intval(substr($headers[0], strlen('HTTP/1.1 '))) == 405) {
+ // The server doesn't support HEAD - emulate it with a GET.
+ $args = func_get_args();
+ $args[1] = 'GET';
+ call_user_func_array(array($this, 'request_streams'), $args);
+ $headers = $this->headers;
+ } else {
+ $headers = $this->parse_header_array($headers, $update_claimed_id);
+ }
+ } else {
+ $headers = array();
+ }
+
+ return $headers;
+ }
+
+ if ($this->verify_peer) {
+ $opts['ssl'] += array(
+ 'verify_peer' => true,
+ 'capath' => $this->capath,
+ 'cafile' => $this->cainfo
+ );
+ }
+
+ $context = stream_context_create ($opts);
+ $data = file_get_contents($url, false, $context);
+ # This is a hack for providers who don't support HEAD requests.
+ # It just creates the headers array for the last request in $this->headers.
+ if(isset($http_response_header)) {
+ $this->headers = $this->parse_header_array($http_response_header, $update_claimed_id);
+ }
+
+ return $data;
+ }
+
+ protected function request($url, $method='GET', $params=array(), $update_claimed_id=false)
+ {
+ if (function_exists('curl_init')
+ && (!in_array('https', stream_get_wrappers()) || !ini_get('safe_mode') && !ini_get('open_basedir'))
+ ) {
+ return $this->request_curl($url, $method, $params, $update_claimed_id);
+ }
+ return $this->request_streams($url, $method, $params, $update_claimed_id);
+ }
+
+ protected function proxy_url()
+ {
+ $result = '';
+
+ if (!empty($this->proxy)) {
+ $result = $this->proxy['host'];
+
+ if (!empty($this->proxy['port'])) {
+ $result = $result . ':' . $this->proxy['port'];
+ }
+
+ if (!empty($this->proxy['user'])) {
+ $result = $this->proxy['user'] . ':' . $this->proxy['pass'] . '@' . $result;
+ }
+
+ $result = 'http://' . $result;
+ }
+
+ return $result;
+ }
+
+ protected function build_url($url, $parts)
+ {
+ if (isset($url['query'], $parts['query'])) {
+ $parts['query'] = $url['query'] . '&' . $parts['query'];
+ }
+
+ $url = $parts + $url;
+ $url = $url['scheme'] . '://'
+ . (empty($url['username'])?''
+ :(empty($url['password'])? "{$url['username']}@"
+ :"{$url['username']}:{$url['password']}@"))
+ . $url['host']
+ . (empty($url['port'])?'':":{$url['port']}")
+ . (empty($url['path'])?'':$url['path'])
+ . (empty($url['query'])?'':"?{$url['query']}")
+ . (empty($url['fragment'])?'':"#{$url['fragment']}");
+ return $url;
+ }
+
+ /**
+ * Helper function used to scan for / tags and extract information
+ * from them
+ */
+ protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName)
+ {
+ preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1);
+ preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2);
+
+ $result = array_merge($matches1[1], $matches2[1]);
+ return empty($result)?false:$result[0];
+ }
+
+ /**
+ * Performs Yadis and HTML discovery. Normally not used.
+ * @param $url Identity URL.
+ * @return String OP Endpoint (i.e. OpenID provider address).
+ * @throws ErrorException
+ */
+ function discover($url)
+ {
+ if (!$url) throw new ErrorException('No identity supplied.');
+ # Use xri.net proxy to resolve i-name identities
+ if (!preg_match('#^https?:#', $url)) {
+ $url = "https://xri.net/$url";
+ }
+
+ # We save the original url in case of Yadis discovery failure.
+ # It can happen when we'll be lead to an XRDS document
+ # which does not have any OpenID2 services.
+ $originalUrl = $url;
+
+ # A flag to disable yadis discovery in case of failure in headers.
+ $yadis = true;
+
+ # Allows optional regex replacement of the URL, e.g. to use Google Apps
+ # as an OpenID provider without setting up XRDS on the domain hosting.
+ if (!is_null($this->xrds_override_pattern) && !is_null($this->xrds_override_replacement)) {
+ $url = preg_replace($this->xrds_override_pattern, $this->xrds_override_replacement, $url);
+ }
+
+ # We'll jump a maximum of 5 times, to avoid endless redirections.
+ for ($i = 0; $i < 5; $i ++) {
+ if ($yadis) {
+ $headers = $this->request($url, 'HEAD', array(), true);
+
+ $next = false;
+ if (isset($headers['x-xrds-location'])) {
+ $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location'])));
+ $next = true;
+ }
+
+ if (isset($headers['content-type'])
+ && (strpos($headers['content-type'], 'application/xrds+xml') !== false
+ || strpos($headers['content-type'], 'text/xml') !== false)
+ ) {
+ # Apparently, some providers return XRDS documents as text/html.
+ # While it is against the spec, allowing this here shouldn't break
+ # compatibility with anything.
+ # ---
+ # Found an XRDS document, now let's find the server, and optionally delegate.
+ $content = $this->request($url, 'GET');
+
+ preg_match_all('#(.*?)#s', $content, $m);
+ foreach($m[1] as $content) {
+ $content = ' ' . $content; # The space is added, so that strpos doesn't return 0.
+
+ # OpenID 2
+ $ns = preg_quote('http://specs.openid.net/auth/2.0/', '#');
+ if(preg_match('#\s*'.$ns.'(server|signon)\s* #s', $content, $type)) {
+ if ($type[1] == 'server') $this->identifier_select = true;
+
+ preg_match('#(.*)#', $content, $server);
+ preg_match('#<(Local|Canonical)ID>(.*)\1ID>#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # Does the server advertise support for either AX or SREG?
+ $this->ax = (bool) strpos($content, 'http://openid.net/srv/ax/1.0 ');
+ $this->sreg = strpos($content, 'http://openid.net/sreg/1.0 ')
+ || strpos($content, 'http://openid.net/extensions/sreg/1.1 ');
+
+ $server = $server[1];
+ if (isset($delegate[2])) $this->identity = trim($delegate[2]);
+ $this->version = 2;
+
+ $this->server = $server;
+ return $server;
+ }
+
+ # OpenID 1.1
+ $ns = preg_quote('http://openid.net/signon/1.1', '#');
+ if (preg_match('#\s*'.$ns.'\s* #s', $content)) {
+
+ preg_match('#(.*)#', $content, $server);
+ preg_match('#<.*?Delegate>(.*)#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # AX can be used only with OpenID 2.0, so checking only SREG
+ $this->sreg = strpos($content, 'http://openid.net/sreg/1.0 ')
+ || strpos($content, 'http://openid.net/extensions/sreg/1.1 ');
+
+ $server = $server[1];
+ if (isset($delegate[1])) $this->identity = $delegate[1];
+ $this->version = 1;
+
+ $this->server = $server;
+ return $server;
+ }
+ }
+
+ $next = true;
+ $yadis = false;
+ $url = $originalUrl;
+ $content = null;
+ break;
+ }
+ if ($next) continue;
+
+ # There are no relevant information in headers, so we search the body.
+ $content = $this->request($url, 'GET', array(), true);
+
+ if (isset($this->headers['x-xrds-location'])) {
+ $url = $this->build_url(parse_url($url), parse_url(trim($this->headers['x-xrds-location'])));
+ continue;
+ }
+
+ $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content');
+ if ($location) {
+ $url = $this->build_url(parse_url($url), parse_url($location));
+ continue;
+ }
+ }
+
+ if (!$content) $content = $this->request($url, 'GET');
+
+ # At this point, the YADIS Discovery has failed, so we'll switch
+ # to openid2 HTML discovery, then fallback to openid 1.1 discovery.
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href');
+ $this->version = 2;
+
+ if (!$server) {
+ # The same with openid 1.1
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href');
+ $this->version = 1;
+ }
+
+ if ($server) {
+ # We found an OpenID2 OP Endpoint
+ if ($delegate) {
+ # We have also found an OP-Local ID.
+ $this->identity = $delegate;
+ }
+ $this->server = $server;
+ return $server;
+ }
+
+ throw new ErrorException("No OpenID Server found at $url", 404);
+ }
+ throw new ErrorException('Endless redirection!', 500);
+ }
+
+ protected function sregParams()
+ {
+ $params = array();
+ # We always use SREG 1.1, even if the server is advertising only support for 1.0.
+ # That's because it's fully backwards compatibile with 1.0, and some providers
+ # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com
+ $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1';
+ if ($this->required) {
+ $params['openid.sreg.required'] = array();
+ foreach ($this->required as $required) {
+ if (!isset(self::$ax_to_sreg[$required])) continue;
+ $params['openid.sreg.required'][] = self::$ax_to_sreg[$required];
+ }
+ $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']);
+ }
+
+ if ($this->optional) {
+ $params['openid.sreg.optional'] = array();
+ foreach ($this->optional as $optional) {
+ if (!isset(self::$ax_to_sreg[$optional])) continue;
+ $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional];
+ }
+ $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']);
+ }
+ return $params;
+ }
+
+ protected function axParams()
+ {
+ $params = array();
+ if ($this->required || $this->optional) {
+ $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
+ $params['openid.ax.mode'] = 'fetch_request';
+ $this->aliases = array();
+ $counts = array();
+ $required = array();
+ $optional = array();
+ foreach (array('required','optional') as $type) {
+ foreach ($this->$type as $alias => $field) {
+ if (is_int($alias)) $alias = strtr($field, '/', '_');
+ $this->aliases[$alias] = 'http://axschema.org/' . $field;
+ if (empty($counts[$alias])) $counts[$alias] = 0;
+ $counts[$alias] += 1;
+ ${$type}[] = $alias;
+ }
+ }
+ foreach ($this->aliases as $alias => $ns) {
+ $params['openid.ax.type.' . $alias] = $ns;
+ }
+ foreach ($counts as $alias => $count) {
+ if ($count == 1) continue;
+ $params['openid.ax.count.' . $alias] = $count;
+ }
+
+ # Don't send empty ax.requied and ax.if_available.
+ # Google and possibly other providers refuse to support ax when one of these is empty.
+ if($required) {
+ $params['openid.ax.required'] = implode(',', $required);
+ }
+ if($optional) {
+ $params['openid.ax.if_available'] = implode(',', $optional);
+ }
+ }
+ return $params;
+ }
+
+ protected function authUrl_v1($immediate)
+ {
+ $returnUrl = $this->returnUrl;
+ # If we have an openid.delegate that is different from our claimed id,
+ # we need to somehow preserve the claimed id between requests.
+ # The simplest way is to just send it along with the return_to url.
+ if($this->identity != $this->claimed_id) {
+ $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ $params = array(
+ 'openid.return_to' => $returnUrl,
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.identity' => $this->identity,
+ 'openid.trust_root' => $this->trustRoot,
+ ) + $this->sregParams();
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ protected function authUrl_v2($immediate)
+ {
+ $params = array(
+ 'openid.ns' => 'http://specs.openid.net/auth/2.0',
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.return_to' => $this->returnUrl,
+ 'openid.realm' => $this->trustRoot,
+ );
+
+ if ($this->ax) {
+ $params += $this->axParams();
+ }
+
+ if ($this->sreg) {
+ $params += $this->sregParams();
+ }
+
+ if (!$this->ax && !$this->sreg) {
+ # If OP doesn't advertise either SREG, nor AX, let's send them both
+ # in worst case we don't get anything in return.
+ $params += $this->axParams() + $this->sregParams();
+ }
+
+ if (!empty($this->oauth) && is_array($this->oauth)) {
+ $params['openid.ns.oauth'] = 'http://specs.openid.net/extensions/oauth/1.0';
+ $params['openid.oauth.consumer'] = str_replace(array('http://', 'https://'), '', $this->trustRoot);
+ $params['openid.oauth.scope'] = implode(' ', $this->oauth);
+ }
+
+ if ($this->identifier_select) {
+ $params['openid.identity'] = $params['openid.claimed_id']
+ = 'http://specs.openid.net/auth/2.0/identifier_select';
+ } else {
+ $params['openid.identity'] = $this->identity;
+ $params['openid.claimed_id'] = $this->claimed_id;
+ }
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ /**
+ * Returns authentication url. Usually, you want to redirect your user to it.
+ * @return String The authentication url.
+ * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1.
+ * @throws ErrorException
+ */
+ function authUrl($immediate = false)
+ {
+ if ($this->setup_url && !$immediate) return $this->setup_url;
+ if (!$this->server) $this->discover($this->identity);
+
+ if ($this->version == 2) {
+ return $this->authUrl_v2($immediate);
+ }
+ return $this->authUrl_v1($immediate);
+ }
+
+ /**
+ * Performs OpenID verification with the OP.
+ * @return Bool Whether the verification was successful.
+ * @throws ErrorException
+ */
+ function validate()
+ {
+ # If the request was using immediate mode, a failure may be reported
+ # by presenting user_setup_url (for 1.1) or reporting
+ # mode 'setup_needed' (for 2.0). Also catching all modes other than
+ # id_res, in order to avoid throwing errors.
+ if(isset($this->data['openid_user_setup_url'])) {
+ $this->setup_url = $this->data['openid_user_setup_url'];
+ return false;
+ }
+ if($this->mode != 'id_res') {
+ return false;
+ }
+
+ $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity'];
+ $params = array(
+ 'openid.assoc_handle' => $this->data['openid_assoc_handle'],
+ 'openid.signed' => $this->data['openid_signed'],
+ 'openid.sig' => $this->data['openid_sig'],
+ );
+
+ if (isset($this->data['openid_ns'])) {
+ # We're dealing with an OpenID 2.0 server, so let's set an ns
+ # Even though we should know location of the endpoint,
+ # we still need to verify it by discovery, so $server is not set here
+ $params['openid.ns'] = 'http://specs.openid.net/auth/2.0';
+ } elseif (isset($this->data['openid_claimed_id'])
+ && $this->data['openid_claimed_id'] != $this->data['openid_identity']
+ ) {
+ # If it's an OpenID 1 provider, and we've got claimed_id,
+ # we have to append it to the returnUrl, like authUrl_v1 does.
+ $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?')
+ . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ if ($this->data['openid_return_to'] != $this->returnUrl) {
+ # The return_to url must match the url of current request.
+ # I'm assuing that noone will set the returnUrl to something that doesn't make sense.
+ return false;
+ }
+
+ $server = $this->discover($this->claimed_id);
+
+ foreach (explode(',', $this->data['openid_signed']) as $item) {
+ # Checking whether magic_quotes_gpc is turned on, because
+ # the function may fail if it is. For example, when fetching
+ # AX namePerson, it might containg an apostrophe, which will be escaped.
+ # In such case, validation would fail, since we'd send different data than OP
+ # wants to verify. stripslashes() should solve that problem, but we can't
+ # use it when magic_quotes is off.
+ $value = $this->data['openid_' . str_replace('.','_',$item)];
+ $params['openid.' . $item] = function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() ? stripslashes($value) : $value;
+
+ }
+
+ $params['openid.mode'] = 'check_authentication';
+
+ $response = $this->request($server, 'POST', $params);
+
+ return preg_match('/is_valid\s*:\s*true/i', $response);
+ }
+
+ protected function getAxAttributes()
+ {
+ $result = array();
+
+ if ($alias = $this->getNamespaceAlias('http://openid.net/srv/ax/1.0', 'ax')) {
+ $prefix = 'openid_' . $alias;
+ $length = strlen('http://axschema.org/');
+
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = $alias . '.type.';
+
+ if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) {
+ continue;
+ }
+
+ $key = substr($key, strlen($keyMatch));
+ $idv = $prefix . '_value_' . $key;
+ $idc = $prefix . '_count_' . $key;
+ $key = substr($this->getItem($prefix . '_type_' . $key), $length);
+
+ if (!empty($key)) {
+ if (($count = intval($this->getItem($idc))) > 0) {
+ $value = array();
+
+ for ($i = 1; $i <= $count; $i++) {
+ $value[] = $this->getItem($idv . '_' . $i);
+ }
+
+ $value = ($count == 1) ? reset($value) : $value;
+ } else {
+ $value = $this->getItem($idv);
+ }
+
+ if (!is_null($value)) {
+ $result[$key] = $value;
+ }
+ }
+ }
+ } else {
+ // No alias for the AX schema has been found,
+ // so there is no AX data in the OP's response.
+ }
+
+ return $result;
+ }
+
+ protected function getSregAttributes()
+ {
+ $attributes = array();
+ $sreg_to_ax = array_flip(self::$ax_to_sreg);
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = 'sreg.';
+ if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) {
+ continue;
+ }
+ $key = substr($key, strlen($keyMatch));
+ if (!isset($sreg_to_ax[$key])) {
+ # The field name isn't part of the SREG spec, so we ignore it.
+ continue;
+ }
+ $attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key];
+ }
+ return $attributes;
+ }
+
+ /**
+ * Gets AX/SREG attributes provided by OP. should be used only after successful validaton.
+ * Note that it does not guarantee that any of the required/optional parameters will be present,
+ * or that there will be no other attributes besides those specified.
+ * In other words. OP may provide whatever information it wants to.
+ * * SREG names will be mapped to AX names.
+ * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email'
+ * @see http://www.axschema.org/types/
+ */
+ function getAttributes()
+ {
+ if (isset($this->data['openid_ns'])
+ && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0'
+ ) { # OpenID 2.0
+ # We search for both AX and SREG attributes, with AX taking precedence.
+ return $this->getAxAttributes() + $this->getSregAttributes();
+ }
+ return $this->getSregAttributes();
+ }
+
+ /**
+ * Gets an OAuth request token if the OpenID+OAuth hybrid protocol has been used.
+ *
+ * In order to use the OpenID+OAuth hybrid protocol, you need to add at least one
+ * scope to the $openid->oauth array before you get the call to getAuthUrl(), e.g.:
+ * $openid->oauth[] = 'https://www.googleapis.com/auth/plus.me';
+ *
+ * Furthermore the registered consumer name must fit the OpenID realm.
+ * To register an OpenID consumer at Google use: https://www.google.com/accounts/ManageDomains
+ *
+ * @return string|bool OAuth request token on success, FALSE if no token was provided.
+ */
+ function getOAuthRequestToken()
+ {
+ $alias = $this->getNamespaceAlias('http://specs.openid.net/extensions/oauth/1.0');
+
+ return !empty($alias) ? $this->data['openid_' . $alias . '_request_token'] : false;
+ }
+
+ /**
+ * Gets the alias for the specified namespace, if it's present.
+ *
+ * @param string $namespace The namespace for which an alias is needed.
+ * @param string $hint Common alias of this namespace, used for optimization.
+ * @return string|null The namespace alias if found, otherwise - NULL.
+ */
+ private function getNamespaceAlias($namespace, $hint = null)
+ {
+ $result = null;
+
+ if (empty($hint) || $this->getItem('openid_ns_' . $hint) != $namespace) {
+ // The common alias is either undefined or points to
+ // some other extension - search for another alias..
+ $prefix = 'openid_ns_';
+ $length = strlen($prefix);
+
+ foreach ($this->data as $key => $val) {
+ if (strncmp($key, $prefix, $length) === 0 && $val === $namespace) {
+ $result = trim(substr($key, $length));
+ break;
+ }
+ }
+ } else {
+ $result = $hint;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Gets an item from the $data array by the specified id.
+ *
+ * @param string $id The id of the desired item.
+ * @return string|null The item if found, otherwise - NULL.
+ */
+ private function getItem($id)
+ {
+ return isset($this->data[$id]) ? $this->data[$id] : null;
+ }
+}
\ No newline at end of file
diff --git a/common/components/nodge/lightopenid/provider/example-mysql.php b/common/components/nodge/lightopenid/provider/example-mysql.php
new file mode 100644
index 0000000..574e3c8
--- /dev/null
+++ b/common/components/nodge/lightopenid/provider/example-mysql.php
@@ -0,0 +1,194 @@
+dh = false;
+ * However, the latter one would disable stateful mode, unless connecting via HTTPS.
+ */
+require 'provider.php';
+
+mysql_connect();
+mysql_select_db('test');
+
+function getUserData($handle=null)
+{
+ if(isset($_POST['login'],$_POST['password'])) {
+ $login = mysql_real_escape_string($_POST['login']);
+ $password = sha1($_POST['password']);
+ $q = mysql_query("SELECT * FROM Users WHERE login = '$login' AND password = '$password'");
+ if($data = mysql_fetch_assoc($q)) {
+ return $data;
+ }
+ if($handle) {
+ echo 'Wrong login/password.';
+ }
+ }
+ if($handle) {
+ ?>
+
+ 'First name',
+ 'namePerson/last' => 'Last name',
+ 'namePerson/friendly' => 'Nickname (login)'
+ );
+
+ private $attrFieldMap = array(
+ 'namePerson/first' => 'firstName',
+ 'namePerson/last' => 'lastName',
+ 'namePerson/friendly' => 'login'
+ );
+
+ function setup($identity, $realm, $assoc_handle, $attributes)
+ {
+ $data = getUserData($assoc_handle);
+ echo '';
+ }
+
+ function checkid($realm, &$attributes)
+ {
+ if(isset($_POST['cancel'])) {
+ $this->cancel();
+ }
+
+ $data = getUserData();
+ if(!$data) {
+ return false;
+ }
+ $realm = mysql_real_escape_string($realm);
+ $q = mysql_query("SELECT attributes FROM AllowedSites WHERE user = '{$data['id']}' AND realm = '$realm'");
+
+ $attrs = array();
+ if($attrs = mysql_fetch_row($q)) {
+ $attrs = explode(',', $attributes[0]);
+ } elseif(isset($_POST['attributes'])) {
+ $attrs = array_keys($_POST['attributes']);
+ } elseif(!isset($_POST['once']) && !isset($_POST['always'])) {
+ return false;
+ }
+
+ $attributes = array();
+ foreach($attrs as $attr) {
+ if(isset($this->attrFieldMap[$attr])) {
+ $attributes[$attr] = $data[$this->attrFieldMap[$attr]];
+ }
+ }
+
+ if(isset($_POST['always'])) {
+ $attrs = mysql_real_escape_string(implode(',', array_keys($attributes)));
+ mysql_query("REPLACE INTO AllowedSites VALUES('{$data['id']}', '$realm', '$attrs')");
+ }
+
+ return $this->serverLocation . '?' . $data['login'];
+ }
+
+ function assoc_handle()
+ {
+ # We generate an integer assoc handle, because it's just faster to look up an integer later.
+ $q = mysql_query("SELECT MAX(id) FROM Associations");
+ $result = mysql_fetch_row($q);
+ return $q[0]+1;
+ }
+
+ function setAssoc($handle, $data)
+ {
+ $data = mysql_real_escape_string(serialize($data));
+ mysql_query("REPLACE INTO Associations VALUES('$handle', '$data')");
+ }
+
+ function getAssoc($handle)
+ {
+ if(!is_numeric($handle)) {
+ return false;
+ }
+ $q = mysql_query("SELECT data FROM Associations WHERE id = '$handle'");
+ $data = mysql_fetch_row($q);
+ if(!$data) {
+ return false;
+ }
+ return unserialize($data[0]);
+ }
+
+ function delAssoc($handle)
+ {
+ if(!is_numeric($handle)) {
+ return false;
+ }
+ mysql_query("DELETE FROM Associations WHERE id = '$handle'");
+ }
+
+}
+$op = new MysqlProvider;
+$op->server();
diff --git a/common/components/nodge/lightopenid/provider/example.php b/common/components/nodge/lightopenid/provider/example.php
new file mode 100644
index 0000000..b8a4c24
--- /dev/null
+++ b/common/components/nodge/lightopenid/provider/example.php
@@ -0,0 +1,53 @@
+select_id = false;
+ }
+ }
+
+ function setup($identity, $realm, $assoc_handle, $attributes)
+ {
+ header('WWW-Authenticate: Basic realm="' . $this->data['openid_realm'] . '"');
+ header('HTTP/1.0 401 Unauthorized');
+ }
+
+ function checkid($realm, &$attributes)
+ {
+ if(!isset($_SERVER['PHP_AUTH_USER'])) {
+ return false;
+ }
+
+ if ($_SERVER['PHP_AUTH_USER'] == $this->login
+ && $_SERVER['PHP_AUTH_PW'] == $this->password
+ ) {
+ # Returning identity
+ # It can be any url that leads here, or to any other place that hosts
+ # an XRDS document pointing here.
+ return $this->serverLocation . '?id=' . $this->login;
+ }
+
+ return false;
+ }
+
+}
+$op = new BasicProvider;
+$op->login = 'test';
+$op->password = 'test';
+$op->server();
diff --git a/common/components/nodge/lightopenid/provider/provider.php b/common/components/nodge/lightopenid/provider/provider.php
new file mode 100644
index 0000000..75164a6
--- /dev/null
+++ b/common/components/nodge/lightopenid/provider/provider.php
@@ -0,0 +1,846 @@
+= 5.1.2
+ *
+ * This is an alpha version, using it in production code is not recommended,
+ * until you are *sure* that it works and is secure.
+ *
+ * Please send me messages about your testing results
+ * (even if successful, so I know that it has been tested).
+ * Also, if you think there's a way to make it easier to use, tell me -- it's an alpha for a reason.
+ * Same thing applies to bugs in code, suggestions,
+ * and everything else you'd like to say about the library.
+ *
+ * There's no usage documentation here, see the examples.
+ *
+ * @author Mewp
+ * @copyright Copyright (c) 2010, Mewp
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ */
+ini_set('error_log','log');
+abstract class LightOpenIDProvider
+{
+ # URL-s to XRDS and server location.
+ public $xrdsLocation, $serverLocation;
+
+ # Should we operate in server, or signon mode?
+ public $select_id = false;
+
+ # Lifetime of an association.
+ protected $assoc_lifetime = 600;
+
+ # Variables below are either set automatically, or are constant.
+ # -----
+ # Can we support DH?
+ protected $dh = true;
+ protected $ns = 'http://specs.openid.net/auth/2.0';
+ protected $data, $assoc;
+
+ # Default DH parameters as defined in the specification.
+ protected $default_modulus;
+ protected $default_gen = 'Ag==';
+
+ # AX <-> SREG transform
+ protected $ax_to_sreg = array(
+ 'namePerson/friendly' => 'nickname',
+ 'contact/email' => 'email',
+ 'namePerson' => 'fullname',
+ 'birthDate' => 'dob',
+ 'person/gender' => 'gender',
+ 'contact/postalCode/home' => 'postcode',
+ 'contact/country/home' => 'country',
+ 'pref/language' => 'language',
+ 'pref/timezone' => 'timezone',
+ );
+
+ # Math
+ private $add, $mul, $pow, $mod, $div, $powmod;
+ # -----
+
+ # ------------------------------------------------------------------------ #
+ # Functions you probably want to implement when extending the class.
+
+ /**
+ * Checks whether an user is authenticated.
+ * The function should determine what fields it wants to send to the RP,
+ * and put them in the $attributes array.
+ * @param Array $attributes
+ * @param String $realm Realm used for authentication.
+ * @return String OP-local identifier of an authenticated user, or an empty value.
+ */
+ abstract function checkid($realm, &$attributes);
+
+ /**
+ * Displays an user interface for inputting user's login and password.
+ * Attributes are always AX field namespaces, with stripped host part.
+ * For example, the $attributes array may be:
+ * array( 'required' => array('namePerson/friendly', 'contact/email'),
+ * 'optional' => array('pref/timezone', 'pref/language')
+ * @param String $identity Discovered identity string. May be used to extract login, unless using $this->select_id
+ * @param String $realm Realm used for authentication.
+ * @param String Association handle. must be sent as openid.assoc_handle in $_GET or $_POST in subsequent requests.
+ * @param Array User attributes requested by the RP.
+ */
+ abstract function setup($identity, $realm, $assoc_handle, $attributes);
+
+ /**
+ * Stores an association.
+ * If you want to use php sessions in your provider code, you have to replace it.
+ * @param String $handle Association handle -- should be used as a key.
+ * @param Array $assoc Association data.
+ */
+ protected function setAssoc($handle, $assoc)
+ {
+ $oldSession = session_id();
+ session_commit();
+ session_id($assoc['handle']);
+ session_start();
+ $_SESSION['assoc'] = $assoc;
+ session_commit();
+ if($oldSession) {
+ session_id($oldSession);
+ session_start();
+ }
+ }
+
+ /**
+ * Retreives association data.
+ * If you want to use php sessions in your provider code, you have to replace it.
+ * @param String $handle Association handle.
+ * @return Array Association data.
+ */
+ protected function getAssoc($handle)
+ {
+ $oldSession = session_id();
+ session_commit();
+ session_id($handle);
+ session_start();
+ $assoc = null;
+ if(!empty($_SESSION['assoc'])) {
+ $assoc = $_SESSION['assoc'];
+ }
+ session_commit();
+ if($oldSession) {
+ session_id($oldSession);
+ session_start();
+ }
+ return $assoc;
+ }
+
+ /**
+ * Deletes an association.
+ * If you want to use php sessions in your provider code, you have to replace it.
+ * @param String $handle Association handle.
+ */
+ protected function delAssoc($handle)
+ {
+ $oldSession = session_id();
+ session_commit();
+ session_id($handle);
+ session_start();
+ session_destroy();
+ if($oldSession) {
+ session_id($oldSession);
+ session_start();
+ }
+ }
+
+ # ------------------------------------------------------------------------ #
+ # Functions that you might want to implement.
+
+ /**
+ * Redirects the user to an url.
+ * @param String $location The url that the user will be redirected to.
+ */
+ protected function redirect($location)
+ {
+ header('Location: ' . $location);
+ die();
+ }
+
+ /**
+ * Generates a new association handle.
+ * @return string
+ */
+ protected function assoc_handle()
+ {
+ return sha1(microtime());
+ }
+
+ /**
+ * Generates a random shared secret.
+ * @return string
+ */
+ protected function shared_secret($hash)
+ {
+ $length = 20;
+ if($hash == 'sha256') {
+ $length = 256;
+ }
+
+ $secret = '';
+ for($i = 0; $i < $length; $i++) {
+ $secret .= mt_rand(0,255);
+ }
+
+ return $secret;
+ }
+
+ /**
+ * Generates a private key.
+ * @param int $length Length of the key.
+ */
+ protected function keygen($length)
+ {
+ $key = '';
+ for($i = 1; $i < $length; $i++) {
+ $key .= mt_rand(0,9);
+ }
+ $key .= mt_rand(1,9);
+
+ return $key;
+ }
+
+ # ------------------------------------------------------------------------ #
+ # Functions that you probably shouldn't touch.
+
+ function __construct()
+ {
+ $this->default_modulus =
+ 'ANz5OguIOXLsDhmYmsWizjEOHTdxfo2Vcbt2I3MYZuYe91ouJ4mLBX+YkcLiemOcPy'
+ . 'm2CBRYHNOyyjmG0mg3BVd9RcLn5S3IHHoXGHblzqdLFEi/368Ygo79JRnxTkXjgmY0'
+ . 'rxlJ5bU1zIKaSDuKdiI+XUkKJX8Fvf8W8vsixYOr';
+
+ $location = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://'
+ . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+ $location = preg_replace('/\?.*/','',$location);
+ $this->serverLocation = $location;
+ $location .= (strpos($location, '?') ? '&' : '?') . 'xrds';
+ $this->xrdsLocation = $location;
+
+ $this->data = $_GET + $_POST;
+
+ # We choose GMP if avaiable, and bcmath otherwise
+ if(function_exists('gmp_add')) {
+ $this->add = 'gmp_add';
+ $this->mul = 'gmp_mul';
+ $this->pow = 'gmp_pow';
+ $this->mod = 'gmp_mod';
+ $this->div = 'gmp_div';
+ $this->powmod = 'gmp_powm';
+ } elseif(function_exists('bcadd')) {
+ $this->add = 'bcadd';
+ $this->mul = 'bcmul';
+ $this->pow = 'bcpow';
+ $this->mod = 'bcmod';
+ $this->div = 'bcdiv';
+ $this->powmod = 'bcpowmod';
+ } else {
+ # If neither are avaiable, we can't use DH
+ $this->dh = false;
+ }
+
+ # However, we do require the hash functions.
+ # They should be built-in anyway.
+ if(!function_exists('hash_algos')) {
+ $this->dh = false;
+ }
+ }
+
+ /**
+ * Displays an XRDS document, or redirects to it.
+ * By default, it detects whether it should display or redirect automatically.
+ * @param bool|null $force When true, always display the document, when false always redirect.
+ */
+ function xrds($force=null)
+ {
+ if($force) {
+ echo $this->xrdsContent();
+ die();
+ } elseif($force === false) {
+ header('X-XRDS-Location: '. $this->xrdsLocation);
+ return;
+ }
+
+ if (isset($_GET['xrds'])
+ || (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/xrds+xml') !== false)
+ ) {
+ header('Content-Type: application/xrds+xml');
+ echo $this->xrdsContent();
+ die();
+ }
+
+ header('X-XRDS-Location: ' . $this->xrdsLocation);
+ }
+
+ /**
+ * Returns the content of the XRDS document
+ * @return String The XRDS document.
+ */
+ protected function xrdsContent()
+ {
+ $lines = array(
+ '',
+ '',
+ '',
+ ' ',
+ ' ' . $this->ns . '/' . ($this->select_id ? 'server' : 'signon') .' ',
+ ' ' . $this->serverLocation . ' ',
+ ' ',
+ ' ',
+ ' '
+ );
+ return implode("\n", $lines);
+ }
+
+ /**
+ * Does everything that a provider has to -- in one function.
+ */
+ function server()
+ {
+ if(isset($this->data['openid_assoc_handle'])) {
+ $this->assoc = $this->getAssoc($this->data['openid_assoc_handle']);
+ if(isset($this->assoc['data'])) {
+ # We have additional data stored for setup.
+ $this->data += $this->assoc['data'];
+ unset($this->assoc['data']);
+ }
+ }
+
+ if (isset($this->data['openid_ns'])
+ && $this->data['openid_ns'] == $this->ns
+ ) {
+ if(!isset($this->data['openid_mode'])) $this->errorResponse();
+
+ switch($this->data['openid_mode'])
+ {
+ case 'checkid_immediate':
+ case 'checkid_setup':
+ $this->checkRealm();
+ # We support AX xor SREG.
+ $attributes = $this->ax();
+ if(!$attributes) {
+ $attributes = $this->sreg();
+ }
+
+ # Even if some user is authenticated, we need to know if it's
+ # the same one that want's to authenticate.
+ # Of course, if we use select_id, we accept any user.
+ if (($identity = $this->checkid($this->data['openid_realm'], $attrValues))
+ && ($this->select_id || $identity == $this->data['openid_identity'])
+ ) {
+ $this->positiveResponse($identity, $attrValues);
+ } elseif($this->data['openid_mode'] == 'checkid_immediate') {
+ $this->redirect($this->response(array('openid.mode' => 'setup_needed')));
+ } else {
+ if(!$this->assoc) {
+ $this->generateAssociation();
+ $this->assoc['private'] = true;
+ }
+ $this->assoc['data'] = $this->data;
+ $this->setAssoc($this->assoc['handle'], $this->assoc);
+ $this->setup($this->data['openid_identity'],
+ $this->data['openid_realm'],
+ $this->assoc['handle'],
+ $attributes);
+ }
+ break;
+ case 'associate':
+ $this->associate();
+ break;
+ case 'check_authentication':
+ $this->checkRealm();
+ if($this->verify()) {
+ echo "ns:$this->ns\nis_valid:true";
+ if(strpos($this->data['openid_signed'],'invalidate_handle') !== false) {
+ echo "\ninvalidate_handle:" . $this->data['openid_invalidate_handle'];
+ }
+ } else {
+ echo "ns:$this->ns\nis_valid:false";
+ }
+ die();
+ break;
+ default:
+ $this->errorResponse();
+ }
+ } else {
+ $this->xrds();
+ }
+ }
+
+ protected function checkRealm()
+ {
+ if (!isset($this->data['openid_return_to'], $this->data['openid_realm'])) {
+ $this->errorResponse();
+ }
+
+ $realm = str_replace('\*', '[^/]', preg_quote($this->data['openid_realm']));
+ if(!preg_match("#^$realm#", $this->data['openid_return_to'])) {
+ $this->errorResponse();
+ }
+ }
+
+ protected function ax()
+ {
+ # Namespace prefix that the fields must have.
+ $ns = 'http://axschema.org/';
+
+ # First, we must find out what alias is used for AX.
+ # Let's check the most likely one
+ $alias = null;
+ if (isset($this->data['openid_ns_ax'])
+ && $this->data['openid_ns_ax'] == 'http://openid.net/srv/ax/1.0'
+ ) {
+ $alias = 'ax';
+ } else {
+ foreach($this->data as $name => $value) {
+ if ($value == 'http://openid.net/srv/ax/1.0'
+ && preg_match('/openid_ns_(.+)/', $name, $m)
+ ) {
+ $alias = $m[1];
+ break;
+ }
+ }
+ }
+
+ if(!$alias) {
+ return null;
+ }
+
+ $fields = array();
+ # Now, we must search again, this time for field aliases
+ foreach($this->data as $name => $value) {
+ if (strpos($name, 'openid_' . $alias . '_type') === false
+ || strpos($value, $ns) === false) {
+ continue;
+ }
+
+ $name = substr($name, strlen('openid_' . $alias . '_type_'));
+ $value = substr($value, strlen($ns));
+
+ $fields[$name] = $value;
+ }
+
+ # Then, we find out what fields are required and optional
+ $required = array();
+ $if_available = array();
+ foreach(array('required','if_available') as $type) {
+ if(empty($this->data["openid_{$alias}_{$type}"])) {
+ continue;
+ }
+ $attributes = explode(',', $this->data["openid_{$alias}_{$type}"]);
+ foreach($attributes as $attr) {
+ if(empty($fields[$attr])) {
+ # There is an undefined field here, so we ignore it.
+ continue;
+ }
+
+ ${$type}[] = $fields[$attr];
+ }
+ }
+
+ $this->data['ax'] = true;
+ return array('required' => $required, 'optional' => $if_available);
+ }
+
+ protected function sreg()
+ {
+ $sreg_to_ax = array_flip($this->ax_to_sreg);
+
+ $attributes = array('required' => array(), 'optional' => array());
+
+ if (empty($this->data['openid_sreg_required'])
+ && empty($this->data['openid_sreg_optional'])
+ ) {
+ return $attributes;
+ }
+
+ foreach(array('required', 'optional') as $type) {
+ foreach(explode(',',$this->data['openid_sreg_' . $type]) as $attr) {
+ if(empty($sreg_to_ax[$attr])) {
+ # Undefined attribute in SREG request.
+ # Shouldn't happen, but we check anyway.
+ continue;
+ }
+
+ $attributes[$type][] = $sreg_to_ax[$attr];
+ }
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * Aids an RP in assertion verification.
+ * @return bool Information whether the verification suceeded.
+ */
+ protected function verify()
+ {
+ # Firstly, we need to make sure that there's an association.
+ # Otherwise the verification will fail,
+ # because we've signed assoc_handle in the assertion
+ if(empty($this->assoc)) {
+ return false;
+ }
+
+ # Next, we check that it's a private association,
+ # i.e. one made without RP input.
+ # Otherwise, the RP shouldn't ask us to verify.
+ if(empty($this->assoc['private'])) {
+ return false;
+ }
+
+ # Now we have to check if the nonce is correct, to prevent replay attacks.
+ if($this->data['openid_response_nonce'] != $this->assoc['nonce']) {
+ return false;
+ }
+
+ # Getting the signed fields for signature.
+ $sig = array();
+ $signed = explode(',', $this->data['openid_signed']);
+ foreach($signed as $field) {
+ $name = strtr($field, '.', '_');
+ if(!isset($this->data['openid_' . $name])) {
+ return false;
+ }
+
+ $sig[$field] = $this->data['openid_' . $name];
+ }
+
+ # Computing the signature and checking if it matches.
+ $sig = $this->keyValueForm($sig);
+ if ($this->data['openid_sig'] !=
+ base64_encode(hash_hmac($this->assoc['hash'], $sig, $this->assoc['mac'], true))
+ ) {
+ return false;
+ }
+
+ # Clearing the nonce, so that it won't be used again.
+ $this->assoc['nonce'] = null;
+
+ if(empty($this->assoc['private'])) {
+ # Commiting changes to the association.
+ $this->setAssoc($this->assoc['handle'], $this->assoc);
+ } else {
+ # Private associations shouldn't be used again, se we can as well delete them.
+ $this->delAssoc($this->assoc['handle']);
+ }
+
+ # Nothing has failed, so the verification was a success.
+ return true;
+ }
+
+ /**
+ * Performs association with an RP.
+ */
+ protected function associate()
+ {
+ # Rejecting no-encryption without TLS.
+ if(empty($_SERVER['HTTPS']) && $this->data['openid_session_type'] == 'no-encryption') {
+ $this->directErrorResponse();
+ }
+
+ # Checking whether we support DH at all.
+ if (!$this->dh && substr($this->data['openid_session_type'], 0, 2) == 'DH') {
+ $this->redirect($this->response(array(
+ 'openid.error' => 'DH not supported',
+ 'openid.error_code' => 'unsupported-type',
+ 'openid.session_type' => 'no-encryption'
+ )));
+ }
+
+ # Creating the association
+ $this->assoc = array();
+ $this->assoc['hash'] = $this->data['openid_assoc_type'] == 'HMAC-SHA256' ? 'sha256' : 'sha1';
+ $this->assoc['handle'] = $this->assoc_handle();
+
+ # Getting the shared secret
+ if($this->data['openid_session_type'] == 'no-encryption') {
+ $this->assoc['mac'] = base64_encode($this->shared_secret($this->assoc['hash']));
+ } else {
+ $this->dh();
+ }
+
+ # Preparing the direct response...
+ $response = array(
+ 'ns' => $this->ns,
+ 'assoc_handle' => $this->assoc['handle'],
+ 'assoc_type' => $this->data['openid_assoc_type'],
+ 'session_type' => $this->data['openid_session_type'],
+ 'expires_in' => $this->assoc_lifetime
+ );
+
+ if(isset($this->assoc['dh_server_public'])) {
+ $response['dh_server_public'] = $this->assoc['dh_server_public'];
+ $response['enc_mac_key'] = $this->assoc['mac'];
+ } else {
+ $response['mac_key'] = $this->assoc['mac'];
+ }
+
+ # ...and sending it.
+ echo $this->keyValueForm($response);
+ die();
+ }
+
+ /**
+ * Creates a private association.
+ */
+ protected function generateAssociation()
+ {
+ $this->assoc = array();
+ # We use sha1 by default.
+ $this->assoc['hash'] = 'sha1';
+ $this->assoc['mac'] = $this->shared_secret('sha1');
+ $this->assoc['handle'] = $this->assoc_handle();
+ }
+
+ /**
+ * Encrypts the MAC key using DH key exchange.
+ */
+ protected function dh()
+ {
+ if(empty($this->data['openid_dh_modulus'])) {
+ $this->data['openid_dh_modulus'] = $this->default_modulus;
+ }
+
+ if(empty($this->data['openid_dh_gen'])) {
+ $this->data['openid_dh_gen'] = $this->default_gen;
+ }
+
+ if(empty($this->data['openid_dh_consumer_public'])) {
+ $this->directErrorResponse();
+ }
+
+ $modulus = $this->b64dec($this->data['openid_dh_modulus']);
+ $gen = $this->b64dec($this->data['openid_dh_gen']);
+ $consumerKey = $this->b64dec($this->data['openid_dh_consumer_public']);
+
+ $privateKey = $this->keygen(strlen($modulus));
+ $publicKey = $this->powmod($gen, $privateKey, $modulus);
+ $ss = $this->powmod($consumerKey, $privateKey, $modulus);
+
+ $mac = $this->x_or(hash($this->assoc['hash'], $ss, true), $this->shared_secret($this->assoc['hash']));
+ $this->assoc['dh_server_public'] = $this->decb64($publicKey);
+ $this->assoc['mac'] = base64_encode($mac);
+ }
+
+ /**
+ * XORs two strings.
+ * @param String $a
+ * @param String $b
+ * @return String $a ^ $b
+ */
+ protected function x_or($a, $b)
+ {
+ $length = strlen($a);
+ for($i = 0; $i < $length; $i++) {
+ $a[$i] = $a[$i] ^ $b[$i];
+ }
+
+ return $a;
+ }
+
+ /**
+ * Prepares an indirect response url.
+ * @param array $params Parameters to be sent.
+ */
+ protected function response($params)
+ {
+ $params += array('openid.ns' => $this->ns);
+ return $this->data['openid_return_to']
+ . (strpos($this->data['openid_return_to'],'?') ? '&' : '?')
+ . http_build_query($params, '', '&');
+ }
+
+ /**
+ * Outputs a direct error.
+ */
+ protected function errorResponse()
+ {
+ if(!empty($this->data['openid_return_to'])) {
+ $response = array(
+ 'openid.mode' => 'error',
+ 'openid.error' => 'Invalid request'
+ );
+ $this->redirect($this->response($response));
+ } else {
+ header('HTTP/1.1 400 Bad Request');
+ $response = array(
+ 'ns' => $this->ns,
+ 'error' => 'Invalid request'
+ );
+ echo $this->keyValueForm($response);
+ }
+ die();
+ }
+
+ /**
+ * Sends an positive assertion.
+ * @param String $identity the OP-Local Identifier that is being authenticated.
+ * @param Array $attributes User attributes to be sent.
+ */
+ protected function positiveResponse($identity, $attributes)
+ {
+ # We generate a private association if there is none established.
+ if(!$this->assoc) {
+ $this->generateAssociation();
+ $this->assoc['private'] = true;
+ }
+
+ # We set openid.identity (and openid.claimed_id if necessary) to our $identity
+ if($this->data['openid_identity'] == $this->data['openid_claimed_id'] || $this->select_id) {
+ $this->data['openid_claimed_id'] = $identity;
+ }
+ $this->data['openid_identity'] = $identity;
+
+ # Preparing fields to be signed
+ $params = array(
+ 'op_endpoint' => $this->serverLocation,
+ 'claimed_id' => $this->data['openid_claimed_id'],
+ 'identity' => $this->data['openid_identity'],
+ 'return_to' => $this->data['openid_return_to'],
+ 'realm' => $this->data['openid_realm'],
+ 'response_nonce' => gmdate("Y-m-d\TH:i:s\Z"),
+ 'assoc_handle' => $this->assoc['handle'],
+ );
+
+ $params += $this->responseAttributes($attributes);
+
+ # Has the RP used an invalid association handle?
+ if (isset($this->data['openid_assoc_handle'])
+ && $this->data['openid_assoc_handle'] != $this->assoc['handle']
+ ) {
+ $params['invalidate_handle'] = $this->data['openid_assoc_handle'];
+ }
+
+ # Signing the $params
+ $sig = hash_hmac($this->assoc['hash'], $this->keyValueForm($params), $this->assoc['mac'], true);
+ $req = array(
+ 'openid.mode' => 'id_res',
+ 'openid.signed' => implode(',', array_keys($params)),
+ 'openid.sig' => base64_encode($sig),
+ );
+
+ # Saving the nonce and commiting the association.
+ $this->assoc['nonce'] = $params['response_nonce'];
+ $this->setAssoc($this->assoc['handle'], $this->assoc);
+
+ # Preparing and sending the response itself
+ foreach($params as $name => $value) {
+ $req['openid.' . $name] = $value;
+ }
+
+ $this->redirect($this->response($req));
+ }
+
+ /**
+ * Prepares an array of attributes to send
+ */
+ protected function responseAttributes($attributes)
+ {
+ if(!$attributes) return array();
+
+ $ns = 'http://axschema.org/';
+
+ $response = array();
+ if(isset($this->data['ax'])) {
+ $response['ns.ax'] = 'http://openid.net/srv/ax/1.0';
+ foreach($attributes as $name => $value) {
+ $alias = strtr($name, '/', '_');
+ $response['ax.type.' . $alias] = $ns . $name;
+ $response['ax.value.' . $alias] = $value;
+ }
+ return $response;
+ }
+
+ foreach($attributes as $name => $value) {
+ if(!isset($this->ax_to_sreg[$name])) {
+ continue;
+ }
+
+ $response['sreg.' . $this->ax_to_sreg[$name]] = $value;
+ }
+ return $response;
+ }
+
+ /**
+ * Encodes fields in key-value form.
+ * @param Array $params Fields to be encoded.
+ * @return String $params in key-value form.
+ */
+ protected function keyValueForm($params)
+ {
+ $str = '';
+ foreach($params as $name => $value) {
+ $str .= "$name:$value\n";
+ }
+
+ return $str;
+ }
+
+ /**
+ * Responds with an information that the user has canceled authentication.
+ */
+ protected function cancel()
+ {
+ $this->redirect($this->response(array('openid.mode' => 'cancel')));
+ }
+
+ /**
+ * Converts base64 encoded number to it's decimal representation.
+ * @param String $str base64 encoded number.
+ * @return String Decimal representation of that number.
+ */
+ protected function b64dec($str)
+ {
+ $bytes = unpack('C*', base64_decode($str));
+ $n = 0;
+ foreach($bytes as $byte) {
+ $n = $this->add($this->mul($n, 256), $byte);
+ }
+
+ return $n;
+ }
+
+ /**
+ * Complements b64dec.
+ */
+ protected function decb64($num)
+ {
+ $bytes = array();
+ while($num) {
+ array_unshift($bytes, $this->mod($num, 256));
+ $num = $this->div($num, 256);
+ }
+
+ if($bytes && $bytes[0] > 127) {
+ array_unshift($bytes,0);
+ }
+
+ array_unshift($bytes, 'C*');
+
+ return base64_encode(call_user_func_array('pack', $bytes));
+ }
+
+ function __call($name, $args)
+ {
+ switch($name) {
+ case 'add':
+ case 'mul':
+ case 'pow':
+ case 'mod':
+ case 'div':
+ case 'powmod':
+ if(function_exists('gmp_strval')) {
+ return gmp_strval(call_user_func_array($this->$name, $args));
+ }
+ return call_user_func_array($this->$name, $args);
+ default:
+ throw new BadMethodCallException();
+ }
+ }
+}
diff --git a/common/components/rules/CommentRule.php b/common/components/rules/CommentRule.php
new file mode 100644
index 0000000..7df4d99
--- /dev/null
+++ b/common/components/rules/CommentRule.php
@@ -0,0 +1,36 @@
+authManager;
+ $access = false;
+ if($params['record']) {
+ $roles = \Yii::$app->user->identity->getRoles();
+ $permissions = [];
+ $queryRole = (new Query())->from('auth_table_access_group')->where(['in', 'role', $roles])->andWhere(['record_id' => $params['record']->primaryKey])->all();
+ $queryUser = (new Query())->from('auth_table_access_user')->where(['user_id' => $user])->andWhere(['record_id' => $params['record']->primaryKey])->all();
+ foreach($queryRole as $oneRole)
+ {
+ $permissions[] = $oneRole['permission'];
+ $permissions = array_merge($permissions, array_keys($auth->getPermissionsByRole($oneRole['permission'])));
+ }
+ foreach($queryUser as $oneUser)
+ {
+ $permissions[] = $oneUser['permission'];
+ $permissions = array_merge($permissions, array_keys($auth->getPermissionsByRole($oneUser['permission'])));
+ }
+ $access = in_array($item->name, array_unique($permissions));
+ }
+ return $access;
+ }
+
+ }
\ No newline at end of file
diff --git a/common/components/rules/DeleteRule.php b/common/components/rules/DeleteRule.php
new file mode 100644
index 0000000..32b8569
--- /dev/null
+++ b/common/components/rules/DeleteRule.php
@@ -0,0 +1,36 @@
+authManager;
+ $access = false;
+ if($params['record']) {
+ $roles = \Yii::$app->user->identity->getRoles();
+ $permissions = [];
+ $queryRole = (new Query())->from('auth_table_access_group')->where(['in', 'role', $roles])->andWhere(['record_id' => $params['record']->primaryKey])->all();
+ $queryUser = (new Query())->from('auth_table_access_user')->where(['user_id' => $user])->andWhere(['record_id' => $params['record']->primaryKey])->all();
+ foreach($queryRole as $oneRole)
+ {
+ $permissions[] = $oneRole['permission'];
+ $permissions = array_merge($permissions, array_keys($auth->getPermissionsByRole($oneRole['permission'])));
+ }
+ foreach($queryUser as $oneUser)
+ {
+ $permissions[] = $oneUser['permission'];
+ $permissions = array_merge($permissions, array_keys($auth->getPermissionsByRole($oneUser['permission'])));
+ }
+ $access = in_array($item->name, array_unique($permissions));
+ }
+ return $access;
+ }
+
+ }
\ No newline at end of file
diff --git a/common/components/rules/UpdateRule.php b/common/components/rules/UpdateRule.php
new file mode 100644
index 0000000..7078f1f
--- /dev/null
+++ b/common/components/rules/UpdateRule.php
@@ -0,0 +1,36 @@
+authManager;
+ $access = false;
+ if($params['record']) {
+ $roles = \Yii::$app->user->identity->getRoles();
+ $permissions = [];
+ $queryRole = (new Query())->from('auth_table_access_group')->where(['in', 'role', $roles])->andWhere(['record_id' => $params['record']->primaryKey])->all();
+ $queryUser = (new Query())->from('auth_table_access_user')->where(['user_id' => $user])->andWhere(['record_id' => $params['record']->primaryKey])->all();
+ foreach($queryRole as $oneRole)
+ {
+ $permissions[] = $oneRole['permission'];
+ $permissions = array_merge($permissions, array_keys($auth->getPermissionsByRole($oneRole['permission'])));
+ }
+ foreach($queryUser as $oneUser)
+ {
+ $permissions[] = $oneUser['permission'];
+ $permissions = array_merge($permissions, array_keys($auth->getPermissionsByRole($oneUser['permission'])));
+ }
+ $access = in_array($item->name, array_unique($permissions));
+ }
+ return $access;
+ }
+
+ }
\ No newline at end of file
diff --git a/common/components/rules/ViewRule.php b/common/components/rules/ViewRule.php
new file mode 100644
index 0000000..0dd3bc0
--- /dev/null
+++ b/common/components/rules/ViewRule.php
@@ -0,0 +1,36 @@
+authManager;
+ $access = false;
+ if($params['record']) {
+ $roles = \Yii::$app->user->identity->getRoles();
+ $permissions = [];
+ $queryRole = (new Query())->from('auth_table_access_group')->where(['in', 'role', $roles])->andWhere(['record_id' => $params['record']->primaryKey])->all();
+ $queryUser = (new Query())->from('auth_table_access_user')->where(['user_id' => $user])->andWhere(['record_id' => $params['record']->primaryKey])->all();
+ foreach($queryRole as $oneRole)
+ {
+ $permissions[] = $oneRole['permission'];
+ $permissions = array_merge($permissions, array_keys($auth->getPermissionsByRole($oneRole['permission'])));
+ }
+ foreach($queryUser as $oneUser)
+ {
+ $permissions[] = $oneUser['permission'];
+ $permissions = array_merge($permissions, array_keys($auth->getPermissionsByRole($oneUser['permission'])));
+ }
+ $access = in_array($item->name, array_unique($permissions));
+ }
+ return $access;
+ }
+
+ }
\ No newline at end of file
diff --git a/common/config/.gitignore b/common/config/.gitignore
new file mode 100644
index 0000000..22258e4
--- /dev/null
+++ b/common/config/.gitignore
@@ -0,0 +1 @@
+main-local.php
\ No newline at end of file
diff --git a/common/config/bootstrap.php b/common/config/bootstrap.php
new file mode 100644
index 0000000..2533ec2
--- /dev/null
+++ b/common/config/bootstrap.php
@@ -0,0 +1,6 @@
+ dirname(dirname(__DIR__)) . '/vendor',
+ 'modules' => [
+ 'permit' => [
+ 'class' => 'app\modules\db_rbac\Yii2DbRbac',
+ 'params' => [
+ 'userClass' => 'common\models\User'
+ ]
+ ],
+ 'blog' => [
+ 'class' => 'common\modules\blog\Module',
+ ],
+ ],
+ 'bootstrap' => [
+ 'options',
+ ],
+ 'components' => [
+ 'cache' => [
+ 'class' => 'yii\caching\FileCache',
+ ],
+ 'urlManager' => [
+ 'enablePrettyUrl' => true,
+ 'showScriptName' => false,
+ 'class'=> 'common\components\LangUrlManager',
+ 'rules'=>[
+ '//' => '//',
+ '///' => '//',
+ ]
+ ],
+ 'request' => [
+ 'class' => 'common\components\LangRequest'
+ ],
+ 'i18n' => [
+ 'translations' => [
+ '*' => [
+ 'class' => 'yii\i18n\PhpMessageSource',
+ 'basePath' => '@common/translation',
+ 'fileMap' => [
+ 'app' => 'app.php',
+ 'app/error' => 'error.php',
+ ],
+ ],
+ 'app' => [
+ 'class' => 'yii\i18n\PhpMessageSource',
+ 'basePath' => '@common/translation',
+ 'fileMap' => [
+ 'app' => 'app.php',
+ 'app/error' => 'error.php',
+ ],
+ ]
+ ],
+ ],
+ 'authManager' => [
+ 'class' => 'yii\rbac\DbManager',
+ ],
+
+ //подключаем конфигурации API соц сетей для авторизации
+
+ 'eauth' => [
+ 'class' => 'nodge\eauth\EAuth',
+ 'popup' => true, // Use the popup window instead of redirecting.
+ 'cache' => false, // Cache component name or false to disable cache. Defaults to 'cache' on production environments.
+ 'cacheExpire' => 0, // Cache lifetime. Defaults to 0 - means unlimited.
+ 'httpClient' => [
+ // uncomment this to use streams in safe_mode
+ //'useStreamsFallback' => true,
+ ],
+ 'services' => [ // You can change the providers and their classes.
+ 'google' => [
+ // register your app here: https://code.google.com/apis/console/
+ 'class' => 'nodge\eauth\services\GoogleOAuth2Service',
+ 'clientId' => 'artbox-1138',
+ 'clientSecret' => '',
+ 'title' => 'Google',
+ ],
+ 'twitter' => [
+ // register your app here: https://dev.twitter.com/apps/new
+ 'class' => 'nodge\eauth\services\TwitterOAuth1Service',
+ 'key' => '8vReLxI63vTs98MBMqhvrszwy',
+ 'secret' => 'jOqNbHIkQw4cVKKJkgrMtaEeCcfbeT1GTik4pF6O9D7AmqcwOG',
+ ],
+ 'yandex' => [
+ // register your app here: https://oauth.yandex.ru/client/my
+ 'class' => 'nodge\eauth\services\YandexOAuth2Service',
+ 'clientId' => 'ea13195ac0424ff8a190838bec41bb71',
+ 'clientSecret' => '911f2c9afcbf4f5f9319b3134c096c86',
+ 'title' => 'Yandex',
+ ],
+ 'facebook' => [
+ // register your app here: https://developers.facebook.com/apps/
+ 'class' => 'nodge\eauth\services\FacebookOAuth2Service',
+ 'clientId' => '1642047622727997',
+ 'clientSecret' => 'f5b7ba4f062a568678b764fc74cc416e',
+ ],
+ 'yahoo' => [
+ 'class' => 'nodge\eauth\services\YahooOpenIDService',
+ //'realm' => '*.example.org', // your domain, can be with wildcard to authenticate on subdomains.
+ ],
+ 'linkedin' => [
+ // register your app here: https://www.linkedin.com/secure/developer
+ 'class' => 'nodge\eauth\services\LinkedinOAuth1Service',
+ 'key' => '77s41eixn3dyvo',
+ 'secret' => '1xLZQ7RRK6RNjo4U',
+ 'title' => 'LinkedIn (OAuth1)',
+ ],
+ 'linkedin_oauth2' => [
+ // register your app here: https://www.linkedin.com/secure/developer
+ 'class' => 'nodge\eauth\services\LinkedinOAuth2Service',
+ 'clientId' => '77s41eixn3dyvo',
+ 'clientSecret' => '1xLZQ7RRK6RNjo4U',
+ 'title' => 'LinkedIn (OAuth2)',
+ ],
+ 'github' => [
+ // register your app here: https://github.com/settings/applications
+ 'class' => 'nodge\eauth\services\GitHubOAuth2Service',
+ 'clientId' => 'd00283b5cfb225cd1600',
+ 'clientSecret' => 'f482361fad7184819d452f421c8b09db60830b42',
+ ],
+ 'live' => [
+ // register your app here: https://account.live.com/developers/applications/index
+ 'class' => 'nodge\eauth\services\LiveOAuth2Service',
+ 'clientId' => '00000000481796AE',
+ 'clientSecret' => 'rt9GiJrlKz3sE6CvdOeuwWyYbl1tQT03',
+ ],
+ 'steam' => [
+ 'class' => 'nodge\eauth\services\SteamOpenIDService',
+ //'realm' => '*.example.org', // your domain, can be with wildcard to authenticate on subdomains.
+ ],
+ 'instagram' => [
+ // register your app here: https://instagram.com/developer/register/
+ 'class' => 'nodge\eauth\services\InstagramOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ ],
+ 'vkontakte' => [
+ // register your app here: https://vk.com/editapp?act=create&site=1
+ 'class' => 'nodge\eauth\services\VKontakteOAuth2Service',
+ 'clientId' => '5155388',
+ 'clientSecret' => 'jxgmdGVQw7huGKRpnX3a',
+ ],
+ 'mailru' => [
+ // register your app here: http://api.mail.ru/sites/my/add
+ 'class' => 'nodge\eauth\services\MailruOAuth2Service',
+ 'clientId' => '739322',
+ 'clientSecret' => 'd6ce7be6ff791375adff58fe0e4460b2',
+ ],
+ 'odnoklassniki' => [
+ // register your app here: http://dev.odnoklassniki.ru/wiki/pages/viewpage.action?pageId=13992188
+ // ... or here: http://www.odnoklassniki.ru/dk?st.cmd=appsInfoMyDevList&st._aid=Apps_Info_MyDev
+ 'class' => 'nodge\eauth\services\OdnoklassnikiOAuth2Service',
+ 'clientId' => '...',
+ 'clientSecret' => '...',
+ 'clientPublic' => '...',
+ 'title' => 'Odnoklas.',
+ ],
+ ],
+ ],
+
+ // (optionally) you can configure logging
+ 'log' => [
+ 'targets' => [
+ [
+ 'class' => 'yii\log\FileTarget',
+ 'logFile' => '@app/runtime/logs/eauth.log',
+ 'categories' => ['nodge\eauth\*'],
+ 'logVars' => [],
+ ],
+ ],
+ ],
+
+ /*========End=======
+ *end api sicial
+ * */
+ 'options' => [
+ 'class' => 'common\models\OptionHelper',
+ ]
+ ],
+ 'language' => 'ru-RU'
+];
diff --git a/common/config/params-local.php b/common/config/params-local.php
new file mode 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/common/config/params-local.php
@@ -0,0 +1,3 @@
+ 'admin@example.com',
+ 'supportEmail' => 'support@example.com',
+ 'user.passwordResetTokenExpire' => 3600,
+
+];
diff --git a/common/mail/layouts/html.php b/common/mail/layouts/html.php
new file mode 100644
index 0000000..bddbc61
--- /dev/null
+++ b/common/mail/layouts/html.php
@@ -0,0 +1,22 @@
+
+beginPage() ?>
+
+
+
+
+ = Html::encode($this->title) ?>
+ head() ?>
+
+
+ beginBody() ?>
+ = $content ?>
+ endBody() ?>
+
+
+endPage() ?>
diff --git a/common/mail/layouts/text.php b/common/mail/layouts/text.php
new file mode 100644
index 0000000..7087cea
--- /dev/null
+++ b/common/mail/layouts/text.php
@@ -0,0 +1,12 @@
+
+beginPage() ?>
+beginBody() ?>
+= $content ?>
+endBody() ?>
+endPage() ?>
diff --git a/common/mail/passwordResetToken-html.php b/common/mail/passwordResetToken-html.php
new file mode 100644
index 0000000..f3daf49
--- /dev/null
+++ b/common/mail/passwordResetToken-html.php
@@ -0,0 +1,15 @@
+urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);
+?>
+
+
Hello = Html::encode($user->username) ?>,
+
+
Follow the link below to reset your password:
+
+
= Html::a(Html::encode($resetLink), $resetLink) ?>
+
diff --git a/common/mail/passwordResetToken-text.php b/common/mail/passwordResetToken-text.php
new file mode 100644
index 0000000..244c0cb
--- /dev/null
+++ b/common/mail/passwordResetToken-text.php
@@ -0,0 +1,12 @@
+urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);
+?>
+Hello = $user->username ?>,
+
+Follow the link below to reset your password:
+
+= $resetLink ?>
diff --git a/common/models/ActiveRecordRule.php b/common/models/ActiveRecordRule.php
new file mode 100644
index 0000000..4e2c618
--- /dev/null
+++ b/common/models/ActiveRecordRule.php
@@ -0,0 +1,44 @@
+authManager && \Yii::$app->options->rule)
+ {
+ $authManager = \Yii::$app->authManager;
+ $roles = \Yii::$app->user->identity->roles;
+ $query->leftJoin ('auth_table_access_group', 'article.article_id = auth_table_access_group.record_id')
+ ->leftJoin ('auth_table_access_user', 'article.article_id = auth_table_access_user.record_id')
+ ->orWhere (['auth_table_access_group.model_name' => self::className (), 'auth_table_access_group.role' => $roles])
+ ->orWhere (['auth_table_access_user.user_id' => \Yii::$app->user->getId(), 'auth_table_access_user.model_name' => self::className ()]);
+ }
+ return $query;
+ }
+
+ public function delete ()
+ {
+ $id = $this->primaryKey;
+ $result = parent::delete();
+ if(is_int($id)) {
+ \Yii::$app->db->createCommand()->delete('auth_table_access_group', ['model_name' => self::className(), 'record_id' => $id])->execute();
+ \Yii::$app->db->createCommand()->delete('auth_table_access_user', ['model_name' => self::className(), 'record_id' => $id])->execute();
+ }
+ return $result;
+ }
+
+ public function update ($runValidation = true, $attributeNames = null)
+ {
+ if(\Yii::$app->user->can('updateRecord', ['record' => $this])) {
+ return parent::update ($runValidation, $attributeNames);
+ } else {
+ throw new ForbiddenHttpException(\Yii::t('app', 'Permission denied'));
+ }
+ }
+ }
\ No newline at end of file
diff --git a/common/models/Catalog.php b/common/models/Catalog.php
new file mode 100644
index 0000000..6498fa8
--- /dev/null
+++ b/common/models/Catalog.php
@@ -0,0 +1,158 @@
+db->createCommand('
+ SELECT
+ termin_structure.termin_id,
+ termin_structure.termin_pid,
+ termin_lang.termin_title
+ FROM termin_structure
+ INNER JOIN termin_lang ON termin_lang.termin_id = termin_structure.termin_id
+ AND termin_lang.language_id = '.Yii::$app->params['language_id'].'
+ ORDER BY termin_structure.termin_id ASC, termin_structure.termin_pid ASC
+ ')->queryAll();
+ }
+
+ /**
+ * Выполняет поиск по параметрам
+ * @param array $param принимает [catalog_id, language_id, return_one, return_field, show_all]
+ * @return array one | array all | string значение масива
+ */
+ public function finInfo (array $params = [])
+ {
+ Tools::ifNotExist ($params, array (
+ 'catalog_id' => false,
+ 'language_id' => false,
+ 'return_one' => false,
+ 'return_field' => false,
+ 'show_all' => false,
+ 'to_array' => true,
+ ));
+
+ $model = new Catalog();
+
+ $query = $model->find()->select('*');
+
+ $query->joinWith(['relationCatalogLang']);
+
+ $WHERE = array ();
+
+ if ($params['catalog_id'])
+ {
+ $WHERE['catalog.catalog_id'] = $params['catalog_id'];
+ }
+
+ if ($params['language_id'])
+ {
+ $WHERE['catalog_i18n.language_id'] = $params['language_id'];
+ }
+
+ if (! empty ($WHERE))
+ {
+ $query->where($WHERE);
+ }
+
+ if ($params['to_array'])
+ {
+ $query = $query->asArray();
+ }
+
+ if ($params['return_one'] || $params['return_field'])
+ {
+
+ $result = $params['return_field'] ? $query->one($params['return_field']) : $query->one();
+ }
+ else
+ {
+ $result = $query->all();
+ }
+
+ return $result;
+ }
+
+ // ===================
+ // ==== STRUCTURE ====
+ // ===================
+
+ var $mass = array ();
+
+ public function build ()
+ {
+ if ($this->mass = self::get ())
+ {
+ return $this->getRecrusive (8);
+ }
+ }
+
+ public function findChild ($id)
+ {
+ $mass = array ();
+
+ foreach ($this->mass as $row)
+ {
+ if ($row['termin_pid'] == $id)
+ {
+ $mass[] = $row;
+ }
+ }
+
+ return $mass;
+ }
+
+ public function getRecrusive ($menu_id)
+ {
+ $items = $this->findChild($menu_id);
+
+ if (! empty ($items))
+ {
+ echo '';
+
+ foreach ($items as $row)
+ {
+ echo ''.$row['termin_title'].' ';
+
+ if ($row['termin_pid'] != 0)
+ {
+ $this->getRecrusive($row['termin_id']);
+ }
+ }
+
+ echo ' ';
+ }
+
+ }
+
+ // =====
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getRelationCatalogLangPlus()
+ {
+ return $this->getRelationCatalogLang()->where(['language_id' => yii::$app->params['language_id']]);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getRelationCatalogLang()
+ {
+ return $this->hasOne(CatalogLang::className(), ['catalog_id' => 'catalog_id']);
+ }
+
+}
\ No newline at end of file
diff --git a/common/models/Language.php b/common/models/Language.php
new file mode 100644
index 0000000..4a98949
--- /dev/null
+++ b/common/models/Language.php
@@ -0,0 +1,99 @@
+language = self::$current->language_code;
+ Yii::$app->params['language_id'] = self::$current->language_id;
+ }
+
+ //Получения объекта языка по умолчанию
+ static function getDefaultLang()
+ {
+ return Language::find()->where('is_default = :default', [':default' => 1])->one();
+ }
+
+ //Получения объекта языка по буквенному идентификатору
+ static function getLangByUrl($lang_code = null)
+ {
+ if ($lang_code === null) {
+ return null;
+ } else {
+ $language = Language::find()->where('language_code = :what', [':what' => $lang_code])->one();
+ return $language === null ? null : $language;
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return 'language';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['language_code', 'language_name', 'country_code'], 'required'],
+ [['language_code', 'country_code'], 'string', 'max' => 4]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'language_id' => Yii::t('app/Lang', 'Language ID'),
+ 'language_code' => Yii::t('app/Lang', 'Lang Code'),
+ 'is_default' => Yii::t('app/Lang', 'Default lang'),
+ 'language_name' => Yii::t('app/Lang', 'Language Name'),
+ 'status' => Yii::t('app/Lang', 'Language Status'),
+ 'country_code' => Yii::t('app/Lang', 'Country Code'),
+ ];
+ }
+
+ public static function getActiveLanguages()
+ {
+ return Language::find()->where(['>=', 'language_id', 1])->andWhere(['status' => 1])->orderBy('is_default DESC')->indexBy('language_id')->all();
+ }
+
+}
diff --git a/common/models/LoginForm.php b/common/models/LoginForm.php
new file mode 100644
index 0000000..eee2366
--- /dev/null
+++ b/common/models/LoginForm.php
@@ -0,0 +1,64 @@
+getUser();
+ if (!$user || !$user->validatePassword($this->password)) {
+ $this->addError('password', 'Incorrect username or password.');
+ }
+ }
+ /**
+ * Logs in a user using the provided username and password.
+ * @return boolean 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);
+ } else {
+ return false;
+ }
+ }
+ /**
+ * Finds user by [[username]]
+ *
+ * @return User|null
+ */
+ private function getUser()
+ {
+ if ($this->_user === false) {
+ $this->_user = User::findByUsername($this->username);
+ }
+ return $this->_user;
+ }
+}
\ No newline at end of file
diff --git a/common/models/Media.php b/common/models/Media.php
new file mode 100644
index 0000000..d66805b
--- /dev/null
+++ b/common/models/Media.php
@@ -0,0 +1,126 @@
+ 'png, gif, jpeg, jpg', 'skipOnEmpty' => true],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'media_id' => Yii::t('app', 'ID'),
+ 'hash' => Yii::t('app', 'Hash'),
+ 'extension' => Yii::t('app', 'Extension'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticleCategoryMedia()
+ {
+ return $this->hasMany(ArticleCategoryMedia::className(), ['media_id' => 'media_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticleMedia()
+ {
+ return $this->hasMany(ArticleMedia::className(), ['media_id' => 'media_id']);
+ }
+
+ public function getArticle()
+ {
+ return $this->hasMany(Article::className(), ['article_id' => 'article_id'])->via('articleMedia');
+ }
+
+ public function upload()
+ {
+ if(!empty($this->imageFile) && $this->validate('imageFile'))
+ {
+ $uploaddir = \Yii::getAlias('@saveImageDir');
+ $this->hash = md5_file($this->imageFile->tempName).\Yii::$app->security->generateRandomString(5);
+ $this->extension = $this->imageFile->extension;
+ if(is_dir($uploaddir.$this->hash)) {
+ return false;
+ } else {
+ if(!mkdir($uploaddir.$this->hash, 0755)) {
+ return false;
+ }
+ }
+ $this->imageFile->saveAs($uploaddir.$this->hash.'/original.'.$this->imageFile->extension, false);
+ if($this->save(false)) {
+ return true;
+ } else {
+ $this->addError('imageFile', \Yii::t('app', 'Cannot load file'));
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public function delete()
+ {
+ $uploaddir = \Yii::getAlias('@saveImageDir');
+ if(is_dir($uploaddir.$this->hash)) {
+ $this->removeDir($uploaddir.$this->hash);
+ }
+ return parent::delete();
+ }
+
+ public function removeDir($dir)
+ {
+ if($objs = glob($dir."/*")) {
+ foreach($objs as $obj) {
+ is_dir($obj) ? removeDir($obj) : unlink($obj);
+ }
+ }
+ rmdir($dir);
+ }
+}
diff --git a/common/models/MenuTree.php b/common/models/MenuTree.php
new file mode 100644
index 0000000..7d2e604
--- /dev/null
+++ b/common/models/MenuTree.php
@@ -0,0 +1,59 @@
+mass = parent::getMenuList ($location_name))
+ {
+ return $this->getMenuRecrusive (0);
+ }
+ }
+
+ public function findChild ($id)
+ {
+ $mass = array ();
+
+ foreach ($this->mass as $row)
+ {
+ if ($row['menu_pid'] == $id)
+ {
+ $mass[] = $row;
+ }
+ }
+
+ return $mass;
+ }
+
+ public function getMenuRecrusive ($menu_id)
+ {
+ $items = $this->findChild($menu_id);
+
+ if (! empty ($items))
+ {
+ $result = [];
+
+ foreach ($items as $row)
+ {
+ $result[] = [
+ 'label' => $row['termin_title'],
+ 'url' => ['/'.$row['termin_alias']],
+ //'url' => [$row['template_file']],
+ //'url' => Url::toRoute($row['template_file']),
+ 'items' => $this->getMenuRecrusive($row['menu_id']),
+ ' ',
+ ];
+ }
+
+ return $result;
+ }
+
+ }
+}
diff --git a/common/models/OptionHelper.php b/common/models/OptionHelper.php
new file mode 100644
index 0000000..4623524
--- /dev/null
+++ b/common/models/OptionHelper.php
@@ -0,0 +1,28 @@
+where(['name' => 'rules'])->with('value');
+ if($return == self::OPTION_OBJECT) {
+ return $result->one();
+ } elseif($return == self::OPTION_ARRAY) {
+ return $result->asArray()->one();
+ } elseif($return == self::OPTION_VALUE) {
+ return $result->one()->value->value;
+ } else {
+ throw new InvalidParamException(Yii::t('app', 'Must be 1-3'));
+ }
+ }
+}
diff --git a/common/models/Social.php b/common/models/Social.php
new file mode 100644
index 0000000..a9669d2
--- /dev/null
+++ b/common/models/Social.php
@@ -0,0 +1,48 @@
+ 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'social_id' => Yii::t('app', 'Social ID'),
+ 'social_name' => Yii::t('app', 'Social Name'),
+ 'social_user_id' => Yii::t('app', 'Social User ID'),
+ 'user_id' => Yii::t('app', 'User ID'),
+ ];
+ }
+}
diff --git a/common/models/SqlQueryBuilder.php b/common/models/SqlQueryBuilder.php
new file mode 100644
index 0000000..116ef54
--- /dev/null
+++ b/common/models/SqlQueryBuilder.php
@@ -0,0 +1,254 @@
+ array(),
+ 'from' => '',
+ 'join' => array(),
+ 'where' => array(),
+ 'group' => array(),
+ 'having' => array(),
+ 'order' => array(),
+ 'limit' => array('offset' => 0, 'limit' => 0),
+ );
+
+ /**
+ * Add fields in query selection
+ *
+ * @param string $fields List of fields to concat to other fields
+ * @return DbQuery
+ */
+ public function select ($fields)
+ {
+ if (! empty ($fields))
+ {
+ $this->query['select'][] = $fields;
+ }
+ return $this;
+ }
+
+ /**
+ * Set table for FROM clause
+ *
+ * @param string $table Table name
+ * @return DbQuery
+ */
+ public function from ($table, $alias = null)
+ {
+ if (! empty ($table))
+ {
+ $this->query['from'][] = '`'.$table.'`'.($alias ? ' '.$alias : '');
+ }
+ return $this;
+ }
+
+ /**
+ * Add JOIN clause
+ * E.g. $this->join('RIGHT JOIN '._DB_PREFIX_.'product p ON ...');
+ *
+ * @param string $join Complete string
+ * @return DbQuery
+ */
+ public function join ($join)
+ {
+ if (! empty ($join))
+ {
+ $this->query['join'][] = $join;
+ }
+ return $this;
+ }
+
+ /**
+ * Add LEFT JOIN clause
+ *
+ * @param string $table Table name (without prefix)
+ * @param string $alias Table alias
+ * @param string $on ON clause
+ */
+ public function leftJoin ($table, $alias = null, $on = null)
+ {
+ return $this->join('LEFT JOIN `'.$table.'`'.($alias ? ' `'.$alias.'`' : '').($on ? ' ON '.$on : ''));
+ }
+
+ /**
+ * Add INNER JOIN clause
+ * E.g. $this->innerJoin('product p ON ...')
+ *
+ * @param string $table Table name (without prefix)
+ * @param string $alias Table alias
+ * @param string $on ON clause
+ */
+ public function innerJoin ($table, $alias = null, $on = null)
+ {
+ return $this->join('INNER JOIN `'.$table.'`'.($alias ? ' '.$alias : '').($on ? ' ON '.$on : ''));
+ }
+
+ /**
+ * Add LEFT OUTER JOIN clause
+ *
+ * @param string $table Table name (without prefix)
+ * @param string $alias Table alias
+ * @param string $on ON clause
+ */
+ public function leftOuterJoin ($table, $alias = null, $on = null)
+ {
+ return $this->join('LEFT OUTER JOIN `'.$table.'`'.($alias ? ' '.$alias : '').($on ? ' ON '.$on : ''));
+ }
+
+ /**
+ * Add NATURAL JOIN clause
+ *
+ * @param string $table Table name (without prefix)
+ * @param string $alias Table alias
+ */
+ public function naturalJoin ($table, $alias = null)
+ {
+ return $this->join('NATURAL JOIN `'.$table.'`'.($alias ? ' '.$alias : ''));
+ }
+
+ /**
+ * Add a restriction in WHERE clause (each restriction will be separated by AND statement)
+ *
+ * @param string $restriction
+ * @return DbQuery
+ */
+ public function where ($restriction)
+ {
+ if (! empty ($restriction))
+ {
+ $this->query['where'][] = $restriction;
+ }
+ return $this;
+ }
+
+ /**
+ * Add a restriction in HAVING clause (each restriction will be separated by AND statement)
+ *
+ * @param string $restriction
+ * @return DbQuery
+ */
+ public function having ($restriction)
+ {
+ if (! empty ($restriction))
+ {
+ $this->query['having'][] = $restriction;
+ }
+ return $this;
+ }
+
+ /**
+ * Add an ORDER B restriction
+ *
+ * @param string $fields List of fields to sort. E.g. $this->order('myField, b.mySecondField DESC')
+ * @return DbQuery
+ */
+ public function orderBy ($fields)
+ {
+ if (! empty ($fields))
+ {
+ $this->query['order'][] = $fields;
+ }
+ return $this;
+ }
+
+ /**
+ * Add a GROUP BY restriction
+ *
+ * @param string $fields List of fields to sort. E.g. $this->group('myField, b.mySecondField DESC')
+ * @return DbQuery
+ */
+ public function groupBy ($fields)
+ {
+ if (! empty ($fields))
+ {
+ $this->query['group'][] = $fields;
+ }
+ return $this;
+ }
+
+ /**
+ * Limit results in query
+ *
+ * @param string $fields List of fields to sort. E.g. $this->order('myField, b.mySecondField DESC')
+ * @return DbQuery
+ */
+ public function limit ($limit, $offset = 0)
+ {
+ $offset = (int)$offset;
+ if ($offset < 0)
+ {
+ $offset = 0;
+ }
+
+ $this->query['limit'] = array(
+ 'offset' => $offset,
+ 'limit' => (int)$limit,
+ );
+ return $this;
+ }
+
+ /**
+ * Generate and get the query
+ *
+ * @return string
+ */
+ public function build ()
+ {
+ $sql = 'SELECT '.((($this->query['select'])) ? implode (",\n", $this->query['select']) : '*')."\n";
+
+ if (! $this->query['from'])
+ {
+ die('DbQuery->build() missing from clause');
+ }
+ $sql .= 'FROM '.implode (', ', $this->query['from'])."\n";
+
+ if ($this->query['join'])
+ {
+ $sql .= implode ("\n", $this->query['join'])."\n";
+ }
+
+ if ($this->query['where'])
+ {
+ $sql .= 'WHERE ('.implode (') AND (', $this->query['where']).")\n";
+ }
+
+ if ($this->query['group'])
+ {
+ $sql .= 'GROUP BY '.implode(', ', $this->query['group'])."\n";
+ }
+
+ if ($this->query['having'])
+ {
+ $sql .= 'HAVING ('.implode (') AND (', $this->query['having']).")\n";
+ }
+
+ if ($this->query['order'])
+ {
+ $sql .= 'ORDER BY '.implode (', ', $this->query['order'])."\n";
+ }
+
+ if ($this->query['limit']['limit'])
+ {
+ $limit = $this->query['limit'];
+ $sql .= 'LIMIT '.(($limit['offset']) ? $limit['offset'].', '.$limit['limit'] : $limit['limit']);
+ }
+/*
+ ob_start();
+ var_dump($sql);
+ echo ob_get_clean();
+*/
+ return $sql;
+ }
+
+ public function __toString ()
+ {
+ return $this->build();
+ }
+}
+
diff --git a/common/models/Tools.php b/common/models/Tools.php
new file mode 100644
index 0000000..0d6baa6
--- /dev/null
+++ b/common/models/Tools.php
@@ -0,0 +1,117 @@
+ $value)
+ {
+ if (is_int ($key))
+ {
+ $exist[$value] = '';
+ unset ($exist[$key]);
+ }
+ }
+
+ foreach ($exist as $key => $value)
+ {
+ if (! isset ($mass[$key])
+ || (isset ($mass[$key]) && $mass[$key] === ''))
+ {
+ $mass[$key] = $value;
+ }
+ }
+ }
+
+ static function translit ($string, $setting = 'all')
+ {
+ $letter = array (
+
+ 'а' => 'a', 'б' => 'b', 'в' => 'v',
+ 'г' => 'g', 'д' => 'd', 'е' => 'e',
+ 'ё' => 'e', 'ж' => 'zh', 'з' => 'z',
+ 'и' => 'i', 'й' => 'y', 'к' => 'k',
+ 'л' => 'l', 'м' => 'm', 'н' => 'n',
+ 'о' => 'o', 'п' => 'p', 'р' => 'r',
+ 'с' => 's', 'т' => 't', 'у' => 'u',
+ 'ф' => 'f', 'х' => 'h', 'ц' => 'c',
+ 'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sch',
+ 'ь' => "", 'ы' => 'y', 'ъ' => "",
+ 'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
+ 'ї' => 'yi', 'є' => 'ye', 'і' => 'ee',
+
+ 'А' => 'A', 'Б' => 'B', 'В' => 'V',
+ 'Г' => 'G', 'Д' => 'D', 'Е' => 'E',
+ 'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z',
+ 'И' => 'I', 'Й' => 'Y', 'К' => 'K',
+ 'Л' => 'L', 'М' => 'M', 'Н' => 'N',
+ 'О' => 'O', 'П' => 'P', 'Р' => 'R',
+ 'С' => 'S', 'Т' => 'T', 'У' => 'U',
+ 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C',
+ 'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch',
+ 'Ь' => "", 'Ы' => 'Y', 'Ъ' => "",
+ 'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya',
+ 'Ї' => 'Yi', 'Є' => 'Ye', 'І' => 'Ee'
+ );
+
+ $symbol = array (
+ ' ' => '-', "'" => '', '"' => '',
+ '!' => '', "@" => '', '#' => '',
+ '$' => '', "%" => '', '^' => '',
+ ';' => '', "*" => '', '(' => '',
+ ')' => '', "+" => '', '~' => '',
+ '.' => '', ',' => '-', '?' => '',
+ '…' => '', '№' => 'N', '°' => '',
+ '`' => '', '|' => '', '&' => '-and-',
+ '<' => '', '>' => ''
+ );
+
+ if ($setting == 'all')
+ {
+ $converter = $letter + $symbol;
+ }
+ else if ($setting == 'letter')
+ {
+ $converter = $letter;
+ }
+ else if ($setting == 'symbol')
+ {
+ $converter = $symbol;
+ }
+
+ $url = strtr ($string, $converter);
+
+ $url = str_replace ("---", '-', $url);
+ $url = str_replace ("--", '-', $url);
+
+ return $url;
+ }
+
+ static function parseUrlParams($params)
+ {
+ $params = preg_split('/,\s*/', $params, -1, PREG_SPLIT_NO_EMPTY);
+ $result = [];
+ if($params) {
+ foreach($params as $param) {
+ $param = preg_split('/\s*=\s*/', $param);
+ if(!empty($param[0]) && !empty($param[1])) {
+ $result[$param[0]] = $param[1];
+ }
+ }
+ }
+ return $result;
+ }
+}
\ No newline at end of file
diff --git a/common/models/User.php b/common/models/User.php
new file mode 100644
index 0000000..e555475
--- /dev/null
+++ b/common/models/User.php
@@ -0,0 +1,256 @@
+ self::STATUS_ACTIVE],
+ ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function findIdentity($id) {
+ if(Yii::$app->getSession()->has('user-'.$id)) {
+ if (Yii::$app->getSession()->has('user-'.$id)) {
+ return new self(Yii::$app->getSession()->get('user-'.$id));
+ }
+ else {
+ return isset(self::$users[$id]) ? new self(self::$users[$id]) : null;
+ }
+ }
+ else {
+ return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
+ }
+
+ }
+ /**
+ * @param \nodge\eauth\ServiceBase $service
+ * @return User
+ * @throws ErrorException
+ */
+ public static function findByEAuth($service) {
+ if (!$service->getIsAuthenticated()) {
+ throw new ErrorException('EAuth user should be authenticated before creating identity.');
+ }
+ $id = $service->getServiceName().'-'.$service->getId();
+ $attributes = array(
+ 'id' => $id,
+ 'username' => $service->getAttribute('name'),
+ 'authKey' => md5($id),
+ 'profile' => $service->getAttributes(),
+ );
+ $attributes['profile']['service'] = $service->getServiceName();
+ Yii::$app->getSession()->set('user-'.$id, $attributes);
+ return new self($attributes);
+ }
+
+ public $authKey;
+
+ /**
+ * @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 out if password reset token is valid
+ *
+ * @param string $token password reset token
+ * @return boolean
+ */
+ 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 boolean 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();
+ }
+
+ /**
+ * Removes password reset token
+ */
+ public function removePasswordResetToken()
+ {
+ $this->password_reset_token = null;
+ }
+
+ public function getUserName()
+ {
+ return $this->username;
+ }
+
+ public function getRoles()
+ {
+ $auth = \Yii::$app->authManager;
+ $roles = $this->getRoleChildrenRecursive($auth->getRolesByUser($this->id), $auth);
+ return $roles;
+ }
+
+ protected function getRoleChildrenRecursive($roles, $auth, $result = [])
+ {
+ if(is_array($roles) && !empty($roles))
+ {
+ foreach($roles as $role => $item)
+ {
+ if(!($item instanceof \yii\rbac\Role)) {
+ continue;
+ }
+ $result[] = $role;
+ $result = self::getRoleChildrenRecursive($auth->getChildren($role), $auth, $result);
+ }
+ return $result;
+ } else {
+ return $result;
+ }
+ }
+}
diff --git a/common/modules/blog/Module.php b/common/modules/blog/Module.php
new file mode 100644
index 0000000..9a800a5
--- /dev/null
+++ b/common/modules/blog/Module.php
@@ -0,0 +1,12 @@
+ [[0 => property(свойство обьекта), ... дополнительные
+ * настройки]], ...[]]
+ *
+ */
+ public $attributes;
+
+ /**
+ * События
+ *
+ * События на которые должно срабатывать поведение. Задается ассоциативный массив, в котором ключ - событие
+ * связанного обьекта, а значение - метод, который вызывается при этом событии
+ *
+ * @return array [key(event) => val(method)]
+ *
+ */
+ public function events()
+ {
+ return [
+ ActiveRecord::EVENT_BEFORE_INSERT => 'autocomplete',
+ ActiveRecord::EVENT_BEFORE_UPDATE => 'autocomplete',
+ ];
+ }
+
+ /**
+ * События
+ *
+ * События на которые должно срабатывать поведение. Задается ассоциативный массив, в котором ключ - событие
+ * связанного обьекта, а значение - метод, который вызывается при этом событии
+ * Доступные автозаполнения:
+ * ['translit' => ['prop1', ... 'prop2']],
+ * где prop - свойство подлежащее транслитерации
+ * ['repeat' => [[string 'prop1', string 'target1', boolean 'skipFilled', int 'count', boolean 'truncate', string 'suffix'], ...[]],
+ * где prop - свойство для преобразования,
+ * target - свойство с которого взять данные,
+ * count - число для преобразования,
+ * skipFilled - пропустить непустые,
+ * truncate - true - обрезать по словам, false - по символам,
+ * suffix - суффикс, который добавить после обрезки
+ *
+ * @param mixed $event Yii обьект свойста https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/concept-events.md
+ *
+ */
+ public function autocomplete($event)
+ {
+ if(!empty($this->attributes['translit'])) {
+ foreach($this->attributes['translit'] as $translit) {
+ if($this->owner->hasAttribute($translit)) {
+ $this->owner->$translit = Tools::translit($this->owner->$translit);
+ }
+ }
+ }
+ if(!empty($this->attributes['repeat'])) {
+ foreach($this->attributes['repeat'] as $repeat) {
+ if(is_array($repeat) && $this->owner->hasAttribute($repeat[0]) && $this->owner->hasAttribute($repeat[1]) && is_int($repeat[3]) && (empty($this->owner->$repeat[0]) || $repeat[2])) {
+ $suffix = $repeat[5]?:'';
+ $truncate = $repeat[4]?'truncateWords':'truncate';
+ $this->owner->$repeat[0] = StringHelper::$truncate($this->owner->$repeat[1], $repeat[3], $suffix);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/modules/blog/config.php b/common/modules/blog/config.php
new file mode 100644
index 0000000..f528ffb
--- /dev/null
+++ b/common/modules/blog/config.php
@@ -0,0 +1,9 @@
+ [
+
+ ],
+ 'params' => [
+ 'test' => 'Hello',
+ ],
+];
diff --git a/common/modules/blog/controllers/AjaxController.php b/common/modules/blog/controllers/AjaxController.php
new file mode 100644
index 0000000..f7002b7
--- /dev/null
+++ b/common/modules/blog/controllers/AjaxController.php
@@ -0,0 +1,132 @@
+request->getIsAjax()) {
+ //throw new ForbiddenHttpException('Permission denied');
+ }
+
+ if(!parent::beforeAction($action)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function actionCategoryForm($language_id, $widget_id)
+ {
+ $model = Language::find()->where(['>=', 'language_id', 1])->andWhere(['status' => 1, 'language_id' => $language_id])->one();
+ if(!$model) {
+ throw new NotFoundHttpException('Language not found');
+ }
+ $category_lang = new ArticleCategoryLang();
+ return $this->renderAjax('_category_form', ['model' => $model, 'category_lang' => $category_lang, 'widget_id' => $widget_id]);
+ }
+
+ public function actionArticleForm($language_id, $widget_id)
+ {
+ $model = Language::find()->where(['>=', 'language_id', 1])->andWhere(['status' => 1, 'language_id' => $language_id])->one();
+ if(!$model) {
+ throw new NotFoundHttpException('Language not found');
+ }
+ $article_lang = new ArticleLang();
+ return $this->renderAjax('_article_form', ['model' => $model, 'article_lang' => $article_lang, 'widget_id' => $widget_id]);
+ }
+
+ public function actionArticleMediaForm($language_id, $widget_id, $type)
+ {
+ $model = Language::find()->where(['>=', 'language_id', 1])->andWhere(['status' => 1, 'language_id' => $language_id])->one();
+ if(!$model) {
+ throw new NotFoundHttpException('Language not found');
+ }
+ if(!in_array($type, ['full', 'preview'])) {
+ throw new InvalidParamException('Type must only be full/preview');
+ }
+ $article_lang = new ArticleMedia();
+ return $this->renderAjax('_article_media_form', ['model' => $model, 'article_lang' => $article_lang, 'widget_id' => $widget_id, 'type' => $type]);
+ }
+
+ public function actionArticleCategoryMediaForm($language_id, $widget_id, $type)
+ {
+ $model = Language::find()->where(['>=', 'language_id', 1])->andWhere(['status' => 1, 'language_id' => $language_id])->one();
+ if(!$model) {
+ throw new NotFoundHttpException('Language not found');
+ }
+ if(!in_array($type, ['full', 'preview'])) {
+ throw new InvalidParamException('Type must only be full/preview');
+ }
+ $article_lang = new ArticleCategoryMedia();
+ return $this->renderAjax('_article_media_form', ['model' => $model, 'article_lang' => $article_lang, 'widget_id' => $widget_id, 'type' => $type]);
+ }
+
+ public function actionRemoveImage()
+ {
+ $post = \Yii::$app->request->post();
+ if(!empty($post['article_media_id'])) {
+ $article_media = ArticleMedia::findOne($post['article_media_id']);
+ if($post['remove_media']) {
+ $media = $article_media->media->delete();
+ }
+ if(!empty($article_media)) {
+ $article_media->delete();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function actionRemoveCategoryImage()
+ {
+ $post = \Yii::$app->request->post();
+ if(!empty($post['category_media_id'])) {
+ $category_media = ArticleCategoryMedia::findOne($post['category_media_id']);
+ if($post['remove_media']) {
+ $media = $category_media->media->delete();
+ }
+ if(!empty($category_media)) {
+ $category_media->delete();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function actionRemoveImageCategory()
+ {
+ $post = \Yii::$app->request->post();
+ if(!empty($post['category_media_id'])) {
+ $category_media = ArticleCategoryMedia::findOne($post['category_media_id']);
+ if($post['remove_media']) {
+ $media = $category_media->media->delete();
+ }
+ if(!empty($category_media)) {
+ $category_media->delete();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function actionMultilangForm($model, $ajaxView, $widget_id, $language_id = NULL)
+ {
+ $model = new $model(['language_id' => $language_id]);
+ return $this->renderAjax($ajaxView, ['model' => $model, 'widget_id' => $widget_id]);
+ }
+}
diff --git a/common/modules/blog/controllers/ArticleController.php b/common/modules/blog/controllers/ArticleController.php
new file mode 100644
index 0000000..5061555
--- /dev/null
+++ b/common/modules/blog/controllers/ArticleController.php
@@ -0,0 +1,228 @@
+ Article::find(),
+ 'pagination' => [
+ 'pageSize' => 1,
+ ],
+ ]);
+ return $this->render('index', ['dataProvider' => $dataProvider]);
+ }
+
+ public function actionCreate()
+ {
+ $article_langs = array();
+ $article = new Article();
+ $default_lang = Language::getDefaultLang();
+ $images = array();
+ $images[$default_lang->language_id]['full'] = new ArticleMedia(['scenario' => ArticleMedia::SCENARIO_FULL]);
+ $images[$default_lang->language_id]['preview'] = new ArticleMedia(['scenario' => ArticleMedia::SCENARIO_PREVIEW]);
+ $images[0]['additional'] = new ArticleMedia(['scenario' => ArticleMedia::SCENARIO_ADDITIONAL]);
+ $article->loadDefaultValues();
+ $langs = Language::getActiveLanguages();
+ $isValid = false;
+ if(!empty(\Yii::$app->request->post())) {
+ $isValid = true;
+ $article->load(\Yii::$app->request->post());
+ $article->user_id = \Yii::$app->user->getId();
+ $isValid = $article->validate();
+ foreach(\Yii::$app->request->post()['ArticleMedia'] as $lang => $value) {
+ foreach($value as $type => $fields) {
+ $images[$lang][$type] = new ArticleMedia(['scenario' => $type]);
+ $images[$lang][$type]->type = $type;
+ $images[$lang][$type]->language_id = $lang;
+ $images[$lang][$type]->imageFile = UploadedFile::getInstance($images[$lang][$type], "[{$lang}][{$type}]imageFile");
+ $isValid = $images[$lang][$type]->validate(['imageFile']) && $isValid;
+ }
+ }
+ $images[0]['additional']->language_id = 0;
+ $images[0]['additional']->type = 'additional';
+ $images[0]['additional']->imageFile = UploadedFile::getInstances($images[0]['additional'], "[0][additional]imageFile");
+ if(empty(\Yii::$app->request->post()['ArticleLang'])) {
+ $article_langs[$default_lang->language_id] = new ArticleLang();
+ $isValid = ArticleLang::validateMultiple($article_langs) && $isValid;
+ } else {
+ foreach(\Yii::$app->request->post()['ArticleLang'] as $index => $article_lang) {
+ $article_langs[$index] = new ArticleLang();
+ }
+ ArticleLang::loadMultiple($article_langs, \Yii::$app->request->post());
+ $isValid = ArticleLang::validateMultiple($article_langs) && $isValid;
+ }
+ } else {
+ $article_langs[$default_lang->language_id] = new ArticleLang();
+ }
+ if($isValid) {
+ $article->save(false);
+ $article_categories = \Yii::$app->request->post('Article')['articleCategoriesArray'];
+ if(!empty($article_categories)) {
+ foreach($article_categories as $article_category) {
+ $articletocategory[$article_category] = new ArticleToCategory();
+ $articletocategory[$article_category]->article_category_id = $article_category;
+ $articletocategory[$article_category]->link('article', $article);
+ }
+ }
+ $first = 1;
+ foreach($images as $lang => $value) {
+ foreach($value as $type => $fields) {
+ $images[$lang][$type]->upload($article->article_id);
+ if($first && $type != 'additional') {
+ $media_clone = clone $images[$lang][$type];
+ $media_clone->setIsNewRecord(true);
+ unset($media_clone->article_media_id);
+ $media_clone->language_id = 0;
+ $media_clone->upload($article->article_id);
+ unset($media_clone);
+ $first = 0;
+ }
+ }
+ }
+ $first = 1;
+ foreach($article_langs as $article_lang) {
+ if($first) {
+ $article_lang_clone = clone $article_lang;
+ $article_lang_clone->language_id = 0;
+ $article_lang_clone->link('article', $article);
+ unset($article_lang_clone);
+ }
+ $article_lang->link('article', $article);
+ $first = 0;
+ }
+ echo "ok";
+ //$this->redirect('index');
+ } else {
+ return $this->render('create', [
+ 'article_langs' => $article_langs,
+ 'article' => $article,
+ 'langs' => $langs,
+ 'images' => $images
+ ]);
+ }
+ }
+
+ public function actionUpdate($id)
+ {
+ $article = Article::findOne($id);
+ $imagestack = $article->getArticleMedia()->all();
+ $images = [];
+ $images[0]['additional'][0] = new ArticleMedia(['scenario' => ArticleMedia::SCENARIO_ADDITIONAL]);
+ $images[0]['additional'][0]->type = 'additional';
+ $images[0]['additional'][0]->language_id = 0;
+ foreach($imagestack as $image) {
+ if(in_array($image->type, ['full', 'preview'])) {
+ $images[$image->language_id][$image->type] = $image;
+ $images[$image->language_id][$image->type]->scenario = $image->type;
+ } else {
+ $images[$image->language_id][$image->type][$image->article_media_id] = $image;
+ $images[$image->language_id][$image->type][$image->article_media_id]->scenario = $image->type;
+ }
+ }
+ foreach($images as $lang => $value) {
+ $images[$lang]['additional'][0] = new ArticleMedia(['scenario' => ArticleMedia::SCENARIO_ADDITIONAL]);
+ }
+ $article_langs = $article->getArticleLangs()->where(['>=', 'language_id', '1'])->indexBy('language_id')->all();
+ $langs = Language::getActiveLanguages();
+ $default_lang = Language::getDefaultLang();
+ $isValid = false;
+ if(!empty(\Yii::$app->request->post())) {
+ $isValid = true;
+ $article->load(\Yii::$app->request->post());
+ ArticleToCategory::deleteAll(['article_id' => $article->article_id]);
+ $article_categories = \Yii::$app->request->post('Article')['articleCategoriesArray'];
+ if(!empty($article_categories)) {
+ foreach($article_categories as $article_category) {
+ $articletocategory[$article_category] = new ArticleToCategory();
+ $articletocategory[$article_category]->article_category_id = $article_category;
+ $articletocategory[$article_category]->link('article', $article);
+ }
+ }
+ $isValid = $article->validate();
+ $images[0]['additional'][0]->type = 'additional';
+ $images[0]['additional'][0]->language_id = 0;
+ $images[0]['additional'][0]->imageFile = UploadedFile::getInstances($images[0]['additional'][0], "[0][additional]imageFile");
+ $isValid = $images[0]['additional'][0]->validate(['imageFile']) && $isValid;
+ foreach(\Yii::$app->request->post()['ArticleMedia'] as $lang => $value) {
+ foreach($value as $type => $fields) {
+ if(!in_array($type, ['full', 'preview'])) continue;
+ $images[$lang][$type] = new ArticleMedia(['scenario' => $type]);
+ $images[$lang][$type]->language_id = $lang;
+ $images[$lang][$type]->type = $type;
+ $images[$lang][$type]->imageFile = UploadedFile::getInstance($images[$lang][$type], "[{$lang}][{$type}]imageFile");
+ $isValid = $images[$lang][$type]->validate(['imageFile']) && $isValid;
+ }
+ }
+ if(empty(\Yii::$app->request->post()['ArticleLang'])) {
+ $isValid = ArticleLang::validateMultiple($article_langs) && $isValid;
+ } else {
+ foreach(\Yii::$app->request->post()['ArticleLang'] as $index => $article_lang) {
+ if (!array_key_exists($index, $article_langs)) {
+ $article_langs[$index] = new ArticleLang();
+ $article_langs[$index]->article_id = $article->article_id;
+ }
+ }
+ ArticleLang::loadMultiple($article_langs, \Yii::$app->request->post());
+ $isValid = ArticleLang::validateMultiple($article_langs) && $isValid;
+ }
+ }
+ if($isValid) {
+ $article->save(false);
+ foreach($images as $lang => $value) {
+ foreach($value as $type => $fields) {
+ if($type == 'additional') {
+ $images[$lang][$type][0]->upload($article->id);
+ } else {
+ if(!empty($images[$lang][$type]->imageFile)) {
+ $images[$lang][$type]->replace($article->article_id);
+ }
+ }
+ }
+ }
+ foreach($article_langs as $article_lang) {
+ $article_lang->save(false);
+ }
+ echo "ok";
+ //$this->redirect('index');
+ } else {
+ return $this->render('update', [
+ 'article_langs' => $article_langs,
+ 'article' => $article,
+ 'langs' => $langs,
+ 'images' => $images
+ ]);
+ }
+ }
+
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+ return $this->redirect(['index']);
+ }
+
+ protected function findModel($id)
+ {
+ if (($model = Article::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/blog/controllers/CategoryController.php b/common/modules/blog/controllers/CategoryController.php
new file mode 100644
index 0000000..7759416
--- /dev/null
+++ b/common/modules/blog/controllers/CategoryController.php
@@ -0,0 +1,219 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post']
+ ]
+ ]
+ ];
+ }
+
+ public function actionIndex()
+ {
+ $dataProvider = new ActiveDataProvider([
+ 'query' => ArticleCategory::find(),
+ 'pagination' => [
+ 'pageSize' => 1,
+ ],
+ ]);
+ return $this->render('index', ['dataProvider' => $dataProvider]);
+ }
+
+ public function actionCreate()
+ {
+ $category_langs = array();
+ $category = new ArticleCategory();
+ $default_lang = Language::getDefaultLang();
+ $images = array();
+ $images[$default_lang->language_id]['full'] = new ArticleCategoryMedia(['scenario' => ArticleCategoryMedia::SCENARIO_FULL]);
+ $images[$default_lang->language_id]['preview'] = new ArticleCategoryMedia(['scenario' => ArticleCategoryMedia::SCENARIO_PREVIEW]);
+ $images[0]['additional'] = new ArticleCategoryMedia(['scenario' => ArticleCategoryMedia::SCENARIO_ADDITIONAL]);
+ $category->loadDefaultValues();
+ $langs = Language::getActiveLanguages();
+ $isValid = false;
+ if(!empty(\Yii::$app->request->post())) {
+ $isValid = true;
+ $category->load(\Yii::$app->request->post());
+ $isValid = $category->validate();
+ foreach(\Yii::$app->request->post()['ArticleCategoryMedia'] as $lang => $value) {
+ foreach($value as $type => $fields) {
+ $images[$lang][$type] = new ArticleCategoryMedia(['scenario' => $type]);
+ $images[$lang][$type]->type = $type;
+ $images[$lang][$type]->language_id = $lang;
+ $images[$lang][$type]->imageFile = UploadedFile::getInstance($images[$lang][$type], "[{$lang}][{$type}]imageFile");
+ $isValid = $images[$lang][$type]->validate(['imageFile']) && $isValid;
+ }
+ }
+ $images[0]['additional']->language_id = 0;
+ $images[0]['additional']->type = 'additional';
+ $images[0]['additional']->imageFile = UploadedFile::getInstances($images[0]['additional'], "[0][additional]imageFile");
+ if(empty(\Yii::$app->request->post()['ArticleCategoryLang'])) {
+ $category_langs[$default_lang->language_id] = new ArticleCategoryLang();
+ $isValid = ArticleCategoryLang::validateMultiple($category_langs) && $isValid;
+ } else {
+ foreach(\Yii::$app->request->post()['ArticleCategoryLang'] as $index => $category_lang) {
+ $category_langs[$index] = new ArticleCategoryLang();
+ }
+ ArticleCategoryLang::loadMultiple($category_langs, \Yii::$app->request->post());
+ $isValid = ArticleCategoryLang::validateMultiple($category_langs) && $isValid;
+ }
+ } else {
+ $category_langs[$default_lang->language_id] = new ArticleCategoryLang();
+ }
+ if($isValid) {
+ $category->save(false);
+ $first = 1;
+ foreach($images as $lang => $value) {
+ foreach($value as $type => $fields) {
+ $images[$lang][$type]->upload($category->article_category_id);
+ if($first && $type != 'additional') {
+ $media_clone = clone $images[$lang][$type];
+ $media_clone->setIsNewRecord(true);
+ unset($media_clone->article_category_media_id);
+ $media_clone->language_id = 0;
+ $media_clone->upload($category->article_category_id);
+ unset($media_clone);
+ $first = 0;
+ }
+ }
+ }
+ $first = 1;
+ foreach($category_langs as $category_lang) {
+ if($first) {
+ $category_lang_clone = clone $category_lang;
+ $category_lang_clone->language_id = 0;
+ $category_lang_clone->link('category', $category);
+ unset($category_lang_clone);
+ }
+ $category_lang->link('category', $category);
+ $first = 0;
+ }
+ echo "ok";
+ //$this->redirect('index');
+ } else {
+ return $this->render('create', [
+ 'category_langs' => $category_langs,
+ 'category' => $category,
+ 'langs' => $langs,
+ 'images' => $images
+ ]);
+ }
+ }
+
+ public function actionUpdate($id)
+ {
+ $category = ArticleCategory::findOne($id);
+ $imagestack = $category->getArticleCategoryMedia()->all();
+ $images = [];
+ $images[0]['additional'][0] = new ArticleCategoryMedia(['scenario' => ArticleCategoryMedia::SCENARIO_ADDITIONAL]);
+ $images[0]['additional'][0]->type = 'additional';
+ $images[0]['additional'][0]->language_id = 0;
+ foreach($imagestack as $image) {
+ if(in_array($image->type, ['full', 'preview'])) {
+ $images[$image->language_id][$image->type] = $image;
+ $images[$image->language_id][$image->type]->scenario = $image->type;
+ } else {
+ $images[$image->language_id][$image->type][$image->article_category_media_id] = $image;
+ $images[$image->language_id][$image->type][$image->article_category_media_id]->scenario = $image->type;
+ }
+ }
+ foreach($images as $lang => $value) {
+ $images[$lang]['additional'][0] = new ArticleCategoryMedia(['scenario' => ArticleCategoryMedia::SCENARIO_ADDITIONAL]);
+ }
+ $category_langs = $category->getArticleCategoryLangs()->where(['>=', 'language_id', '1'])->indexBy('language_id')->all();
+ $langs = Language::getActiveLanguages();
+ $default_lang = Language::getDefaultLang();
+ $isValid = false;
+ if(!empty(\Yii::$app->request->post())) {
+ $isValid = true;
+ $category->load(\Yii::$app->request->post());
+ $isValid = $category->validate();
+ $images[0]['additional'][0]->type = 'additional';
+ $images[0]['additional'][0]->language_id = 0;
+ $images[0]['additional'][0]->imageFile = UploadedFile::getInstances($images[0]['additional'][0], "[0][additional]imageFile");
+ $isValid = $images[0]['additional'][0]->validate(['imageFile']) && $isValid;
+ foreach(\Yii::$app->request->post()['ArticleCategoryMedia'] as $lang => $value) {
+ foreach($value as $type => $fields) {
+ if(!in_array($type, ['full', 'preview'])) continue;
+ $images[$lang][$type] = new ArticleCategoryMedia(['scenario' => $type]);
+ $images[$lang][$type]->language_id = $lang;
+ $images[$lang][$type]->type = $type;
+ $images[$lang][$type]->imageFile = UploadedFile::getInstance($images[$lang][$type], "[{$lang}][{$type}]imageFile");
+ $isValid = $images[$lang][$type]->validate(['imageFile']) && $isValid;
+ }
+ }
+ if(empty(\Yii::$app->request->post()['ArticleCategoryLang'])) {
+ $isValid = ArticleCategoryLang::validateMultiple($category_langs) && $isValid;
+ } else {
+ foreach(\Yii::$app->request->post()['ArticleCategoryLang'] as $index => $category_lang) {
+ if(!array_key_exists($index, $category_langs)) {
+ $category_langs[$index] = new ArticleCategoryLang();
+ $category_langs[$index]->article_category_id = $category->article_category_id;
+ }
+ }
+ ArticleCategoryLang::loadMultiple($category_langs, \Yii::$app->request->post());
+ $isValid = ArticleCategoryLang::validateMultiple($category_langs) && $isValid;
+ }
+ }
+ if($isValid) {
+ $category->save(false);
+ foreach($images as $lang => $value) {
+ foreach($value as $type => $fields) {
+ if($type == 'additional') {
+ $images[$lang][$type][0]->upload($category->article_category_id);
+ } else {
+ if(!empty($images[$lang][$type]->imageFile)) {
+ $images[$lang][$type]->replace($category->article_category_id);
+ }
+ }
+ }
+ }
+ foreach($category_langs as $category_lang) {
+ $category_lang->save(false);
+ }
+ echo "ok";
+ //$this->redirect('index');
+ } else {
+ return $this->render('update', [
+ 'category_langs' => $category_langs,
+ 'category' => $category,
+ 'langs' => $langs,
+ 'images' => $images
+ ]);
+ }
+ }
+
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+ return $this->redirect(['index']);
+ }
+
+ protected function findModel($id)
+ {
+ if (($model = ArticleCategory::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/blog/controllers/DefaultController.php b/common/modules/blog/controllers/DefaultController.php
new file mode 100644
index 0000000..3e2294c
--- /dev/null
+++ b/common/modules/blog/controllers/DefaultController.php
@@ -0,0 +1,12 @@
+render('index');
+ }
+}
diff --git a/common/modules/blog/controllers/MediaController.php b/common/modules/blog/controllers/MediaController.php
new file mode 100644
index 0000000..e1d91f5
--- /dev/null
+++ b/common/modules/blog/controllers/MediaController.php
@@ -0,0 +1,45 @@
+request->isPost) {
+ $model->imageFile = UploadedFile::getInstance($model, 'imageFile');
+ if($model->upload()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return $this->render('index', ['model' => $model]);
+ }
+
+ public function actionCreate()
+ {
+
+ }
+
+ public function actionUpdate($id)
+ {
+
+ }
+
+ public function actionDelete($id)
+ {
+ $model = Media::findOne($id);
+ return $model->delete();
+ }
+
+ protected function findModel($id)
+ {
+
+ }
+}
diff --git a/common/modules/blog/controllers/TestController.php b/common/modules/blog/controllers/TestController.php
new file mode 100644
index 0000000..5f77485
--- /dev/null
+++ b/common/modules/blog/controllers/TestController.php
@@ -0,0 +1,17 @@
+language_id] = new ArticleLang();
+ $model[3] = new ArticleLang();
+ return $this->render('index', ['model' => $model]);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/blog/models/Article.php b/common/modules/blog/models/Article.php
new file mode 100644
index 0000000..85584b1
--- /dev/null
+++ b/common/modules/blog/models/Article.php
@@ -0,0 +1,163 @@
+ Autocomplete::className(),
+ 'attributes' => [
+ 'translit' => ['code'],
+ ]
+ ]
+ ];
+ }
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['sort', 'article_pid', 'status', 'comment', 'vote'], 'integer'],
+ [['date_add', 'date_update'], 'safe'],
+ [['code'], 'required'],
+ [['code', 'tag'], 'string']
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'article_id' => Yii::t('app', 'ID'),
+ 'sort' => Yii::t('app', 'Sort'),
+ 'date_add' => Yii::t('app', 'Create At'),
+ 'date_update' => Yii::t('app', 'Update At'),
+ 'code' => Yii::t('app', 'Code'),
+ 'user_id' => Yii::t('app', 'Author'),
+ 'tag' => Yii::t('app', 'Tags'),
+ 'article_pid' => Yii::t('app', 'Parent ID'),
+ 'status' => Yii::t('app', 'Active'),
+ 'comment' => Yii::t('app', 'Comments'),
+ 'vote' => Yii::t('app', 'Voting'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getParent()
+ {
+ return $this->hasOne(Article::className(), ['article_id' => 'article_pid']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticles()
+ {
+ return $this->hasMany(Article::className(), ['article_pid' => 'article_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getUser()
+ {
+ return $this->hasOne(User::className(), ['id' => 'user_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticleLangs()
+ {
+ return $this->hasMany(ArticleLang::className(), ['article_id' => 'article_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticleMedia()
+ {
+ return $this->hasMany(ArticleMedia::className(), ['article_id' => 'article_id']);
+ }
+
+ public function getMedia()
+ {
+ return $this->hasMany(Media::className(), ['article_id' => 'media_id'])->via('articleMedia');
+ }
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticleToCategories()
+ {
+ return $this->hasMany(ArticleToCategory::className(), ['article_id' => 'article_id']);
+ }
+
+ public function getArticleCategories()
+ {
+ return $this->hasMany(ArticleCategory::className(), ['article_category_id' => 'article_category_id'])->viaTable('article_to_category', ['article_id' => 'article_category_id']);
+ }
+
+ public static function findArticleDropdown($id)
+ {
+ $query = new Query();
+ return $query->select(['l.name', 'a.article_id'])
+ ->from(['article a'])
+ ->leftJoin(['article_lang l'], 'a.article_id = l.article_id')
+ ->where(['l.language_id' => 0, 'a.status' => 1])
+ ->andWhere(['not', ['a.article_id' => $id]])
+ ->indexBy('article_id')
+ ->column();
+ }
+
+ public function getArticleCategoriesArray()
+ {
+ return $this->getArticleToCategories()->select('article_category_id')->column();
+ }
+
+}
diff --git a/common/modules/blog/models/ArticleCategory.php b/common/modules/blog/models/ArticleCategory.php
new file mode 100644
index 0000000..f91721f
--- /dev/null
+++ b/common/modules/blog/models/ArticleCategory.php
@@ -0,0 +1,133 @@
+ Autocomplete::className(),
+ 'attributes' => [
+ 'translit' => ['code'],
+ ]
+ ]
+ ];
+ }
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['status', 'sort', 'article_category_pid'], 'integer'],
+ [['code'], 'required'],
+ [['code', 'tag'], 'string'],
+ [['date_add', 'date_update'], 'safe'],
+ [['status'], 'boolean'],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'article_category_id' => Yii::t('app', 'ID'),
+ 'status' => Yii::t('app', 'Active'),
+ 'sort' => Yii::t('app', 'Sort'),
+ 'code' => Yii::t('app', 'Code'),
+ 'date_add' => Yii::t('app', 'Created At'),
+ 'date_update' => Yii::t('app', 'Updated At'),
+ 'tag' => Yii::t('app', 'Tags'),
+ 'article_category_pid' => Yii::t('app', 'Parent ID'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticles()
+ {
+ return $this->hasMany(Article::className(), ['article_id' => 'article_id'])->viaTable('article_to_category', ['article_category_id' => 'article_category_id']) ;
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getParent()
+ {
+ return $this->hasOne(ArticleCategory::className(), ['article_category_id' => 'article_category_pid']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticleCategories()
+ {
+ return $this->hasMany(ArticleCategory::className(), ['article_category_pid' => 'article_category_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticleCategoryLangs()
+ {
+ return $this->hasMany(ArticleCategoryLang::className(), ['article_category_id' => 'article_category_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticleCategoryMedia()
+ {
+ return $this->hasMany(ArticleCategoryMedia::className(), ['article_category_id' => 'article_category_id']);
+ }
+
+ public static function findArticleCategoryDropdown($id)
+ {
+ $query = new Query();
+ return $query->select(['l.name', 'c.article_category_id'])
+ ->from(['article_category c'])
+ ->leftJoin(['article_category_lang l'], 'c.article_category_id = l.article_category_id')
+ ->where(['l.language_id' => 0, 'c.status' => 1])
+ ->andWhere(['not', ['c.article_category_id' => $id]])
+ ->indexBy('article_category_id')
+ ->column();
+ }
+
+}
diff --git a/common/modules/blog/models/ArticleCategoryLang.php b/common/modules/blog/models/ArticleCategoryLang.php
new file mode 100644
index 0000000..1851437
--- /dev/null
+++ b/common/modules/blog/models/ArticleCategoryLang.php
@@ -0,0 +1,102 @@
+ Autocomplete::className(),
+ 'attributes' => [
+ 'repeat' => [['preview', 'text', false, 5, true, '...']],
+ ]
+ ]
+ ];
+ }
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['language_id', 'article_category_id'], 'integer'],
+ [['text', 'name'], 'required'],
+ [['text', 'preview', 'seo_url', 'name', 'meta_title', 'meta_descr', 'meta_keyword', 'h1_tag', 'tag'], 'string'],
+ ['seo_url', function($attribute, $params) {
+ $pattern = "/^[a-zA-Z\d_-]+$/";
+ if(!preg_match($pattern, $this->$attribute)) {
+ $this->addError($attribute, Yii::t('app', "Pattern doesn't match."));
+ }
+ }]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'article_category_language_id' => Yii::t('app', 'ID'),
+ 'language_id' => Yii::t('app', 'Lang ID'),
+ 'article_category_id' => Yii::t('app', 'Category ID'),
+ 'text' => Yii::t('app', 'Text'),
+ 'preview' => Yii::t('app', 'Preview'),
+ 'seo_url' => Yii::t('app', 'Seo Url'),
+ 'name' => Yii::t('app', 'Name'),
+ 'meta_title' => Yii::t('app', 'Meta Title'),
+ 'meta_descr' => Yii::t('app', 'Meta Descr'),
+ 'meta_keyword' => Yii::t('app', 'Meta Keywords'),
+ 'h1_tag' => Yii::t('app', 'H1 Tag'),
+ 'tag' => Yii::t('app', 'Tags'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getCategory()
+ {
+ return $this->hasOne(ArticleCategory::className(), ['article_category_id' => 'article_category_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getLang()
+ {
+ return $this->hasOne(Language::className(), ['language_id' => 'language_id']);
+ }
+}
diff --git a/common/modules/blog/models/ArticleCategoryMedia.php b/common/modules/blog/models/ArticleCategoryMedia.php
new file mode 100644
index 0000000..a923910
--- /dev/null
+++ b/common/modules/blog/models/ArticleCategoryMedia.php
@@ -0,0 +1,162 @@
+ 10],
+ [['imageFile'], 'file', 'extensions' => 'png, gif, jpg, jpeg', 'skipOnEmpty' => true, 'on' => self::SCENARIO_FULL],
+ [['imageFile'], 'file', 'extensions' => 'png, gif, jpg, jpeg', 'skipOnEmpty' => true, 'on' => self::SCENARIO_PREVIEW],
+ [['imageFile'], 'file', 'extensions' => 'png, gif, jpg, jpeg', 'skipOnEmpty' => true, 'maxFiles' => 10, 'on' => self::SCENARIO_ADDITIONAL]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'article_category_media_id' => Yii::t('app', 'ID'),
+ 'article_category_id' => Yii::t('app', 'Category ID'),
+ 'media_id' => Yii::t('app', 'Media ID'),
+ 'media_alt' => Yii::t('app', 'Media Alt'),
+ 'media_title' => Yii::t('app', 'Media Title'),
+ 'media_caption' => Yii::t('app', 'Media Caption'),
+ 'type' => Yii::t('app', 'Type'),
+ 'imageFile' => Yii::t('app', 'Image File'),
+ 'language_id' => Yii::t('app', 'Language ID'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getCategory()
+ {
+ return $this->hasOne(ArticleCategory::className(), ['article_category_id' => 'article_category_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getMedia()
+ {
+ return $this->hasOne(Media::className(), ['media_id' => 'media_id']);
+ }
+
+ public function upload($category_id)
+ {
+ $this->article_category_id = $category_id;
+ if(is_array($this->imageFile)) {
+ $ok = true;
+ foreach($this->imageFile as $image) {
+ $media_category = clone $this;
+ $media = new Media();
+ $media->imageFile = $image;
+ $media->upload();
+ $media_category->media_id = $media->media_id;
+ $ok = $media_category->save() && $ok;
+ unset($media_category);
+ }
+ return $ok;
+ } elseif(!empty($this->imageFile)) {
+ $media = new Media();
+ $media->imageFile = $this->imageFile;
+ $media->upload();
+ $this->media_id = $media->media_id;
+ return $this->save();
+ }
+ }
+
+ public function replace($category_id, $removeMedia = false)
+ {
+ $this->article_category_id = $category_id;
+ if($removeMedia) {
+ $category_media = ArticleCategoryMedia::find()->select('media_id')->where(['article_category_id' => $this->article_category_id, 'type' => $this->type])->column();
+ $media = array();
+ foreach($category_media as $media_id) {
+ $media[] = Media::findOne(['media_id' => $media_id]);
+ }
+ $media = array_unique($media);
+ foreach($media as $one_media) {
+ if($one_media instanceof Media) {
+ $one_media->delete();
+ }
+ }
+ unset($media);
+ unset($category_media);
+ }
+ if(is_array($this->imageFile)) {
+ $ok = true;
+ foreach($this->imageFile as $image) {
+ $media_category = clone $this;
+ $media = new Media();
+ $media->imageFile = $image;
+ $media->upload();
+ $media_category->media_id = $media->media_id;
+ $ok = $media_category->save() && $ok;
+ unset($media_category);
+ }
+ return $ok;
+ } elseif(!empty($this->imageFile)) {
+ ArticleCategoryMedia::deleteAll(['category_id' => $this->article_category_id, 'type' => $this->type]);
+ $media = new Media();
+ $media->imageFile = $this->imageFile;
+ $media->upload();
+ $this->media_id = $media->media_id;
+ $this->setIsNewRecord(true);
+ return $this->save();
+ }
+ }
+
+}
diff --git a/common/modules/blog/models/ArticleLang.php b/common/modules/blog/models/ArticleLang.php
new file mode 100644
index 0000000..378b266
--- /dev/null
+++ b/common/modules/blog/models/ArticleLang.php
@@ -0,0 +1,85 @@
+ Yii::t('app', 'ID'),
+ 'language_id' => Yii::t('app', 'Lang ID'),
+ 'article_id' => Yii::t('app', 'Article ID'),
+ 'text' => Yii::t('app', 'Text'),
+ 'seo_url' => Yii::t('app', 'Seo Url'),
+ 'name' => Yii::t('app', 'Name'),
+ 'preview' => Yii::t('app', 'Preview'),
+ 'meta_title' => Yii::t('app', 'Meta Title'),
+ 'meta_descr' => Yii::t('app', 'Meta Descr'),
+ 'meta_keyword' => Yii::t('app', 'Meta Keywords'),
+ 'h1_tag' => Yii::t('app', 'H1 Tag'),
+ 'tag' => Yii::t('app', 'Tags'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticle()
+ {
+ return $this->hasOne(Article::className(), ['article_id' => 'article_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getLang()
+ {
+ return $this->hasOne(Language::className(), ['language_id' => 'language_id']);
+ }
+}
diff --git a/common/modules/blog/models/ArticleMedia.php b/common/modules/blog/models/ArticleMedia.php
new file mode 100644
index 0000000..42ccfa0
--- /dev/null
+++ b/common/modules/blog/models/ArticleMedia.php
@@ -0,0 +1,161 @@
+ 10],
+ [['imageFile'], 'file', 'extensions' => 'png, gif, jpg, jpeg', 'skipOnEmpty' => true, 'on' => self::SCENARIO_FULL],
+ [['imageFile'], 'file', 'extensions' => 'png, gif, jpg, jpeg', 'skipOnEmpty' => true, 'on' => self::SCENARIO_PREVIEW],
+ [['imageFile'], 'file', 'extensions' => 'png, gif, jpg, jpeg', 'skipOnEmpty' => true, 'maxFiles' => 10, 'on' => self::SCENARIO_ADDITIONAL]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'article_media_id' => Yii::t('app', 'ID'),
+ 'article_id' => Yii::t('app', 'Article ID'),
+ 'media_id' => Yii::t('app', 'Media ID'),
+ 'type' => Yii::t('app', 'Type'),
+ 'media_alt' => Yii::t('app', 'Media Alt'),
+ 'media_title' => Yii::t('app', 'Media Title'),
+ 'media_caption' => Yii::t('app', 'Media Caption'),
+ 'imageFile' => Yii::t('app', 'Image File'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticle()
+ {
+ return $this->hasOne(Article::className(), ['article_id' => 'article_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getMedia()
+ {
+ return $this->hasOne(Media::className(), ['media_id' => 'media_id']);
+ }
+
+ public function upload($article_id)
+ {
+ $this->article_id = $article_id;
+ if(is_array($this->imageFile)) {
+ $ok = true;
+ foreach($this->imageFile as $image) {
+ $media_article = clone $this;
+ $media = new Media();
+ $media->imageFile = $image;
+ $media->upload();
+ $media_article->media_id = $media->media_id;
+ $ok = $media_article->save() && $ok;
+ unset($media_article);
+ }
+ return $ok;
+ } elseif(!empty($this->imageFile)) {
+ $media = new Media();
+ $media->imageFile = $this->imageFile;
+ $media->upload();
+ $this->media_id = $media->media_id;
+ return $this->save();
+ }
+ }
+
+ public function replace($article_id, $removeMedia = false)
+ {
+ $this->article_id = $article_id;
+ if($removeMedia && !$this->getIsNewRecord()) {
+ $article_media = ArticleMedia::find()->select('media_id')->where(['article_id' => $this->article_id, 'type' => $this->type, 'language_id' => $this->language_id])->column();
+ $media = array();
+ foreach($article_media as $media_id) {
+ $media[] = Media::findOne(['media_id' => $media_id]);
+ }
+ $media = array_unique($media);
+ foreach($media as $one_media) {
+ if($one_media instanceof Media) {
+ $one_media->delete();
+ }
+ }
+ unset($media);
+ unset($article_media);
+ }
+ if(is_array($this->imageFile)) {
+ $ok = true;
+ foreach($this->imageFile as $image) {
+ $media_article = clone $this;
+ $media = new Media();
+ $media->imageFile = $image;
+ $media->upload();
+ $media_article->media_id = $media->media_id;
+ $ok = $media_article->save() && $ok;
+ unset($media_article);
+ }
+ return $ok;
+ } elseif(!empty($this->imageFile)) {
+ ArticleMedia::deleteAll(['article_id' => $this->article_id, 'type' => $this->type, 'language_id' => $this->language_id]);
+ $media = new Media();
+ $media->imageFile = $this->imageFile;
+ $media->upload();
+ $this->media_id = $media->media_id;
+ $this->setIsNewRecord(true);
+ return $this->save();
+ }
+ }
+
+}
diff --git a/common/modules/blog/models/ArticleToCategory.php b/common/modules/blog/models/ArticleToCategory.php
new file mode 100644
index 0000000..f665d84
--- /dev/null
+++ b/common/modules/blog/models/ArticleToCategory.php
@@ -0,0 +1,62 @@
+ Yii::t('app', 'Article ID'),
+ 'article_category_id' => Yii::t('app', 'Category ID'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getArticle()
+ {
+ return $this->hasOne(Article::className(), ['article_id' => 'article_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getCategory()
+ {
+ return $this->hasOne(ArticleCategory::className(), ['article_category_id' => 'article_category_id']);
+ }
+}
diff --git a/common/modules/blog/views/ajax/_article_form.php b/common/modules/blog/views/ajax/_article_form.php
new file mode 100644
index 0000000..47e23d4
--- /dev/null
+++ b/common/modules/blog/views/ajax/_article_form.php
@@ -0,0 +1,38 @@
+
+
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]language_id"]))->label(false)->hiddenInput(['value' => $model->language_id]) ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]text", 'form' => $form]))->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ]]); ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]preview", 'form' => $form]))->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ]]); ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]seo_url"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]name"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]meta_title"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]meta_descr"]))->textarea() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]meta_keyword"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]h1_tag"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]tag"]))->textInput() ?>
+
+
+end();
+ }
+?>
diff --git a/common/modules/blog/views/ajax/_article_form_test.php b/common/modules/blog/views/ajax/_article_form_test.php
new file mode 100644
index 0000000..fbadb64
--- /dev/null
+++ b/common/modules/blog/views/ajax/_article_form_test.php
@@ -0,0 +1,38 @@
+
+
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]language_id"]))->label(false)->hiddenInput(['value' => $model->language_id]) ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]text", 'form' => $form]))->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ]]); ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]preview", 'form' => $form]))->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ]]); ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]seo_url"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]name"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]meta_title"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]meta_descr"]))->textarea() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]meta_keyword"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]h1_tag"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $model, 'attribute' => "[$model->language_id]tag"]))->textInput() ?>
+
+
+end();
+ }
+?>
diff --git a/common/modules/blog/views/ajax/_article_media_form.php b/common/modules/blog/views/ajax/_article_media_form.php
new file mode 100644
index 0000000..8ff29f3
--- /dev/null
+++ b/common/modules/blog/views/ajax/_article_media_form.php
@@ -0,0 +1,17 @@
+
+
+
+ = (new ActiveField(['model' => $article_lang, 'attribute' => "[$model->language_id][$type]language_id"]))->label(false)->hiddenInput(['value' => $model->language_id]) ?>
+
+ = (new ActiveField(['model' => $article_lang, 'attribute' => "[$model->language_id][$type]imageFile"]))->fileInput(['class' => 'image_inputs_field']) ?>
+
+
+end();
+?>
diff --git a/common/modules/blog/views/ajax/_category_form.php b/common/modules/blog/views/ajax/_category_form.php
new file mode 100644
index 0000000..521317b
--- /dev/null
+++ b/common/modules/blog/views/ajax/_category_form.php
@@ -0,0 +1,33 @@
+
+
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]language_id"]))->label(false)->hiddenInput(['value' => $model->language_id]) ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]text", 'form' => $form]))->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ]]); ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]preview", 'form' => $form]))->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ]]); ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]seo_url"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]name"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]meta_title"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]meta_descr"]))->textarea() ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]meta_keyword"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]h1_tag"]))->textInput() ?>
+
+ = (new ActiveField(['model' => $category_lang, 'attribute' => "[$model->language_id]tag"]))->textInput() ?>
+
+
+end();
+?>
diff --git a/common/modules/blog/views/article/_form.php b/common/modules/blog/views/article/_form.php
new file mode 100644
index 0000000..30eb077
--- /dev/null
+++ b/common/modules/blog/views/article/_form.php
@@ -0,0 +1,172 @@
+
+
+
+ ['enctype' => 'multipart/form-data']]); ?>
+
+ = $form->field($article, 'code')->hint(Yii::t('app', 'Insensitive latin non-space'))->textInput() ?>
+
+ = $form->field($article, 'tag')->hint(Yii::t('app', 'Comma-separated'))->textInput() ?>
+
+ = $form->field($article, 'sort')->input('number') ?>
+
+ = $form->field($article, 'article_pid')
+ ->dropDownList(Article::findArticleDropdown($article->article_id), ['prompt' => Yii::t('app', 'Select parent')]) ?>
+
+ = $form->field($article, 'articleCategoriesArray')
+ ->dropDownList(ArticleCategory::findArticleCategoryDropdown(NULL), ['multiple' => 'multiple'])->label(\Yii::t('app', 'Article Categories Array')); ?>
+
+ = $form->field($article, 'status')->checkbox() ?>
+
+
+
+
+ Url::to(['/blog/ajax/article-media-form?type=full']),
+ 'form' => $form,
+ 'data_langs' => $article->getIsNewRecord()?$images:ArticleMedia::find()->where(['article_id' => $article->article_id, 'type' => 'full'])->indexBy('language_id')->all()
+ ]);
+ $first = 1;
+ foreach($images as $lang => $value) {
+ if(!array_key_exists('full', $value)) continue;
+ ?>
+
+ field($images[$lang]['full'], "[{$lang}][full]language_id")->label(false)->hiddenInput(['value' => $lang]);
+ echo $form->field($images[$lang]['full'], "[{$lang}][full]imageFile")->fileInput(['class' => 'image_inputs_field']);
+ if(!empty($images[$lang]['full']->article_media_id)) {
+ echo "
media->hash}/original.{$images[$lang]['full']->media->extension}' width='100' class='image_inputs_prev'>";
+ }
+ ?>
+
+ end();
+ ?>
+
+
+ Url::to(['/blog/ajax/article-media-form?type=preview']),
+ 'form' => $form,
+ 'data_langs' => $article->getIsNewRecord()?$images:ArticleMedia::find()->where(['article_id' => $article->article_id, 'type' => 'preview'])->indexBy('language_id')->all()
+ ]);
+ $first = 1;
+ foreach($images as $lang => $value) {
+ if(!array_key_exists('preview', $value)) continue;
+ ?>
+
+ field($images[$lang]['preview'], "[{$lang}][preview]language_id")->label(false)->hiddenInput(['value' => $lang]);
+ echo $form->field($images[$lang]['preview'], "[{$lang}][preview]imageFile")->fileInput(['class' => 'image_inputs_field']);
+ if(!empty($images[$lang]['preview']->article_media_id)) {
+ echo "
media->hash}/original.{$images[$lang]['preview']->media->extension}' width='100' class='image_inputs_prev'>";
+ }
+ ?>
+
+ end();
+ ?>
+
+
+ field(is_array($images[0]['additional'])?$images[0]['additional'][0]:$images[0]['additional'], "[0][additional]imageFile[]")->fileInput(['multiple' => 'multiple', 'class' => 'image_inputs_field']);
+ if(is_array($images[0]['additional']) && count($images[0]['additional']) > 1) {
+ foreach($images[0]['additional'] as $onefield => $oneimage) {
+ if($onefield) {
+ ?>
+
+
+
+
+
+
+
+ $article_langs,
+ 'form' => $form,
+ 'ajaxView' => '@common/modules/blog/views/ajax/_article_form',
+ ]);
+ /*
+ $multilang = Multilang::begin(['ajaxpath' => Url::to(['/blog/ajax/article-form']), 'form' => $form, 'data_langs' => $article_langs]);
+ ?>
+ $article_lang) {
+ ?>
+
+
+ = $form->field($article_langs[$index], "[$index]language_id")->label(false)->hiddenInput(['value' => $index]) ?>
+
+ = $form->field($article_langs[$index], "[$index]text")->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ], ]); ?>
+
+ = $form->field($article_langs[$index], "[$index]preview")->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ], ]); ?>
+
+ = $form->field($article_langs[$index], "[$index]seo_url")->textInput() ?>
+
+ = $form->field($article_langs[$index], "[$index]name")->textInput() ?>
+
+ = $form->field($article_langs[$index], "[$index]meta_title")->textInput() ?>
+
+ = $form->field($article_langs[$index], "[$index]meta_descr")->textarea(); ?>
+
+ = $form->field($article_langs[$index], "[$index]meta_keywords")->textInput() ?>
+
+ = $form->field($article_langs[$index], "[$index]h1_tag")->textInput() ?>
+
+ = $form->field($article_langs[$index], "[$index]tags")->textInput() ?>
+
+
+
+ end();
+ */
+ ?>
+
+
+ = Html::submitButton($article->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $article->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/modules/blog/views/article/create.php b/common/modules/blog/views/article/create.php
new file mode 100644
index 0000000..6cd9da6
--- /dev/null
+++ b/common/modules/blog/views/article/create.php
@@ -0,0 +1,19 @@
+title = Yii::t('app', 'Article create');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Articles'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'article_langs' => $article_langs,
+ 'article' => $article,
+ 'langs' => $langs,
+ 'images' => $images
+ ]) ?>
+
+
diff --git a/common/modules/blog/views/article/index.php b/common/modules/blog/views/article/index.php
new file mode 100644
index 0000000..ce83f97
--- /dev/null
+++ b/common/modules/blog/views/article/index.php
@@ -0,0 +1,32 @@
+ $dataProvider,
+ 'columns' => [
+ 'article_id',
+ 'code',
+ 'date_add',
+ [
+ 'value' => function($data) {
+ return $data->user->firstname.' '.$data->user->lastname;
+ },
+ 'header' => Yii::t('app', 'Author')
+ ],
+ [
+ 'class' => Column::className(),
+ 'header' => Yii::t('app', 'Name'),
+ 'content' => function($model, $key, $index, $column) {
+ return $model->getArticleLangs()->where(['language_id' => Language::getDefaultLang()->language_id])->one()->name;
+ }
+ ],
+ [
+ 'class' => ActionColumn::className(),
+ 'template' => '{update} {delete}'
+ ]
+ ]
+]);
\ No newline at end of file
diff --git a/common/modules/blog/views/article/update.php b/common/modules/blog/views/article/update.php
new file mode 100644
index 0000000..4458a2b
--- /dev/null
+++ b/common/modules/blog/views/article/update.php
@@ -0,0 +1,18 @@
+title = Yii::t('app', 'Article update');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Articles'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ = $this->render('_form', [
+ 'article_langs' => $article_langs,
+ 'article' => $article,
+ 'langs' => $langs,
+ 'images' => $images
+ ]) ?>
+
+
diff --git a/common/modules/blog/views/category/_form.php b/common/modules/blog/views/category/_form.php
new file mode 100644
index 0000000..001da73
--- /dev/null
+++ b/common/modules/blog/views/category/_form.php
@@ -0,0 +1,158 @@
+
+
+
+ ['enctype' => 'multipart/form-data']]); ?>
+
+ = $form->field($category, 'code')->hint(Yii::t('app', 'Insensitive latin non-space'))->textInput() ?>
+
+ = $form->field($category, 'tag')->hint(Yii::t('app', 'Comma-separated'))->textInput() ?>
+
+ = $form->field($category, 'sort')->input('number') ?>
+
+ = $form->field($category, 'article_category_pid')
+ ->dropDownList(ArticleCategory::findArticleCategoryDropdown($category->article_category_id), ['prompt' => Yii::t('app', 'Select parent')]) ?>
+
+ = $form->field($category, 'status')->checkbox() ?>
+
+
+
+
+ Url::to(['/blog/ajax/article-category-media-form?type=full']),
+ 'form' => $form,
+ 'data_langs' => $category->getIsNewRecord()?$images:ArticleCategoryMedia::find()->where(['article_category_id' => $category->article_category_id, 'type' => 'full'])->indexBy('language_id')->all()
+ ]);
+ $first = 1;
+ foreach($images as $lang => $value) {
+ if(!array_key_exists('full', $value)) continue;
+ ?>
+
+ field($images[$lang]['full'], "[{$lang}][full]language_id")->label(false)->hiddenInput(['value' => $lang]);
+ echo $form->field($images[$lang]['full'], "[{$lang}][full]imageFile")->fileInput(['class' => 'image_inputs_field']);
+ if(!empty($images[$lang]['full']->article_category_media_id)) {
+ echo "
media->hash}/original.{$images[$lang]['full']->media->extension}' width='100' class='image_inputs_prev'>";
+ }
+ ?>
+
+ end();
+ ?>
+
+
+ Url::to(['/blog/ajax/article-category-media-form?type=preview']),
+ 'form' => $form,
+ 'data_langs' => $category->getIsNewRecord()?$images:ArticleCategoryMedia::find()->where(['article_category_id' => $category->article_category_id, 'type' => 'preview'])->indexBy('language_id')->all()
+ ]);
+ $first = 1;
+ foreach($images as $lang => $value) {
+ if(!array_key_exists('preview', $value)) continue;
+ ?>
+
+ field($images[$lang]['preview'], "[{$lang}][preview]language_id")->label(false)->hiddenInput(['value' => $lang]);
+ echo $form->field($images[$lang]['preview'], "[{$lang}][preview]imageFile")->fileInput(['class' => 'image_inputs_field']);
+ if(!empty($images[$lang]['preview']->article_category_media_id)) {
+ echo "
media->hash}/original.{$images[$lang]['preview']->media->extension}' width='100' class='image_inputs_prev'>";
+ }
+ ?>
+
+ end();
+ ?>
+
+
+ field(is_array($images[0]['additional'])?$images[0]['additional'][0]:$images[0]['additional'], "[0][additional]imageFile[]")->fileInput(['multiple' => 'multiple', 'class' => 'image_inputs_field']);
+ if(is_array($images[0]['additional']) && count($images[0]['additional']) > 1) {
+ foreach($images[0]['additional'] as $onefield => $oneimage) {
+ if($onefield) {
+ ?>
+
+
+
+
+
+
+
+ Url::to(['/blog/ajax/category-form']), 'form' => $form, 'data_langs' => $category_langs])
+ ?>
+ $category_lang) {
+ ?>
+
+ = $form->field($category_langs[$index], "[$index]language_id")->label(false)->hiddenInput(['value' => $index]) ?>
+
+ = $form->field($category_langs[$index], "[$index]text")->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ], ]); ?>
+
+ = $form->field($category_langs[$index], "[$index]preview")->widget(CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ], ]); ?>
+
+ = $form->field($category_langs[$index], "[$index]seo_url")->textInput() ?>
+
+ = $form->field($category_langs[$index], "[$index]name")->textInput() ?>
+
+ = $form->field($category_langs[$index], "[$index]meta_title")->textInput() ?>
+
+ = $form->field($category_langs[$index], "[$index]meta_descr")->textarea(); ?>
+
+ = $form->field($category_langs[$index], "[$index]meta_keyword")->textInput() ?>
+
+ = $form->field($category_langs[$index], "[$index]h1_tag")->textInput() ?>
+
+ = $form->field($category_langs[$index], "[$index]tag")->textInput() ?>
+
+
+
+ end();
+ ?>
+
+
+ = Html::submitButton($category->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $category->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/modules/blog/views/category/create.php b/common/modules/blog/views/category/create.php
new file mode 100644
index 0000000..265a30d
--- /dev/null
+++ b/common/modules/blog/views/category/create.php
@@ -0,0 +1,19 @@
+title = Yii::t('app', 'Category create');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Categories'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'category_langs' => $category_langs,
+ 'category' => $category,
+ 'langs' => $langs,
+ 'images' => $images
+ ]) ?>
+
+
diff --git a/common/modules/blog/views/category/index.php b/common/modules/blog/views/category/index.php
new file mode 100644
index 0000000..f5e1d9e
--- /dev/null
+++ b/common/modules/blog/views/category/index.php
@@ -0,0 +1,26 @@
+ $dataProvider,
+ 'columns' => [
+ 'article_category_id',
+ 'code',
+ 'date_add',
+ 'date_update',
+ [
+ 'class' => Column::className(),
+ 'header' => Yii::t('app', 'Name'),
+ 'content' => function($model, $key, $index, $column) {
+ return $model->getArticleCategoryLangs()->orderBy(['language_id' => 'ASC'])->one()->name;
+ }
+ ],
+ [
+ 'class' => ActionColumn::className(),
+ 'template' => '{update} {delete}'
+ ]
+ ]
+]);
\ No newline at end of file
diff --git a/common/modules/blog/views/category/update.php b/common/modules/blog/views/category/update.php
new file mode 100644
index 0000000..353c73e
--- /dev/null
+++ b/common/modules/blog/views/category/update.php
@@ -0,0 +1,18 @@
+title = Yii::t('app', 'Update category');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Categories'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ = $this->render('_form', [
+ 'category_langs' => $category_langs,
+ 'category' => $category,
+ 'langs' => $langs,
+ 'images' => $images
+ ]) ?>
+
+
diff --git a/common/modules/blog/views/default/index.php b/common/modules/blog/views/default/index.php
new file mode 100644
index 0000000..17d1a81
--- /dev/null
+++ b/common/modules/blog/views/default/index.php
@@ -0,0 +1,7 @@
+value);
\ No newline at end of file
diff --git a/common/modules/blog/views/media/index.php b/common/modules/blog/views/media/index.php
new file mode 100644
index 0000000..b931b6b
--- /dev/null
+++ b/common/modules/blog/views/media/index.php
@@ -0,0 +1,37 @@
+ ['enctype' => 'multipart/form-data']]);
+
+echo $form->field($model, 'imageFile')->fileInput(['multiple' => 'multiple']);
+
+?>
+
+
+ = Html::submitButton(Yii::t('app', 'Create'), ['class' => 'btn btn-success']) ?>
+
+
+
diff --git a/common/modules/blog/views/test/index.php b/common/modules/blog/views/test/index.php
new file mode 100644
index 0000000..7244adf
--- /dev/null
+++ b/common/modules/blog/views/test/index.php
@@ -0,0 +1,11 @@
+ $model,
+ 'form' => $form,
+ 'ajaxView' => '@common/modules/blog/views/ajax/_article_form_test',
+]);
+$form->end();
\ No newline at end of file
diff --git a/common/translation/ru/app.php b/common/translation/ru/app.php
new file mode 100644
index 0000000..b313076
--- /dev/null
+++ b/common/translation/ru/app.php
@@ -0,0 +1,100 @@
+ 'Добавить',
+ 'Settings' => 'Настройки',
+ 'languages' => 'Языки',
+ 'Languages' => 'Языки',
+ 'Language list' => 'Список языков',
+ 'roles' => 'Роли',
+ 'Create Language' => 'Добавить язык',
+ 'Cancel' => 'Отмена',
+ 'Make default' => 'Установить по умолчанию',
+ 'Language Name' => 'Название языка',
+ 'Lang Code' => 'Код языка',
+ 'Is Default' => 'По умолчанию',
+ 'adress' => 'Адрес',
+ 'adress_name' => 'Адрес',
+ 'Create adress' => 'Добавить адрес',
+ 'adres' => 'Адрес',
+ 'x' => 'Координата x',
+ 'y' => 'Координата y',
+ 'phone' => 'Телефон',
+ 'name' => 'Название',
+ 'one_name' => 'Имя',
+ 'message' => 'Сообщение',
+ 'Feedback' => 'Обратная связь',
+ 'Pages' => 'Страницы',
+ 'Articles' => 'Статьи',
+ 'Article update' => 'Редактирование статьи',
+ 'Code' => 'Идентификатор',
+ 'Tags' => 'Тэги',
+ 'Sort' => 'Сортировка',
+ 'Parent ID' => 'Родительская запись',
+ 'Article Categories Array' => 'Родительские категории',
+ 'Active' => 'Активность',
+ 'full' => 'Детальное изображение',
+ 'preview' => 'Миниатюрное изображение',
+ 'additional' => 'Дополнительные изображения',
+ 'Image File' => 'Изображение',
+ 'Text' => 'Текст записи детально',
+ 'Preview' => 'Текст записи кратко',
+ 'Seo Url' => 'Seo Url',
+ 'Name' => 'Название',
+ 'Meta Title' => 'Meta Title',
+ 'Meta Descr' => 'Meta Description',
+ 'Meta Keywords' => 'Meta Keywords',
+ 'H1 Tag' => 'H1 тэг',
+ 'Create At' => 'Дата создания',
+ 'Update At' => 'Дата изменения',
+ 'Author' => 'Автор',
+ 'Created At' => 'Дата создания',
+ 'Updated At' => 'Дата изменения',
+ 'Add language' => 'Добавить язык',
+ 'Categories' => 'Категории',
+ 'Category create' => 'Создание категории',
+ 'Category update' => 'Редактирование категории',
+ 'Article create' => 'Создание статьи',
+ 'Update category' => 'Редактирование категории',
+ 'Select parent' => 'Выбрать родителя',
+ 'Blog' => 'Блог',
+ 'Static pages' => 'Статические страницы',
+ 'Create Admin Menu' => 'Создать элемент меню',
+ 'Admin Menus' => 'Административное меню',
+ 'Hide Min' => 'Спрятать в свернутом',
+ 'Path' => 'Путь',
+ 'Params' => 'Параметры',
+ 'Parent item' => 'Родительский элемент',
+ 'Active Menu' => 'Активный',
+ 'Not Active Menu' => 'Неактивный',
+ 'Show Menu Min' => 'Отобразить',
+ 'Hide Menu Min' => 'Спрятать',
+ 'Update' => 'Редактировать',
+ 'Delete' => 'Удалить',
+ 'Settings categories' => 'Разделы настроек',
+ 'Back' => 'Назад',
+
+ // Вова
+ 'page' => 'Страница',
+ 'date_add' => 'Дата добавления',
+ 'template' => 'Шаблон',
+ 'image' => 'Картинка',
+ 'title' => 'Заголовок',
+ 'meta_title' => 'Meta Title',
+ 'meta_description' => 'Meta Description',
+ 'text' => 'Текст',
+ 'page_alias' => 'alias',
+ 'language_id' => 'ID языка',
+ 'common' => 'Общее',
+ 'lang' => 'Языковые переменные',
+ 'termin' => 'Термин',
+ 'related' => 'Связанные',
+ 'menu' => 'Меню',
+ 'location' => 'Разположение',
+ 'book' => 'Справочник',
+
+ // Дима
+
+];
\ No newline at end of file
diff --git a/common/translation/uk/app.php b/common/translation/uk/app.php
new file mode 100644
index 0000000..9795107
--- /dev/null
+++ b/common/translation/uk/app.php
@@ -0,0 +1,27 @@
+ 'Сторінка',
+ 'date_add' => 'Дата додання',
+ 'template' => 'Шаблон',
+ 'image' => 'Картинка',
+ 'title' => 'Заголовок',
+ 'meta_title' => 'Meta Title',
+ 'meta_description' => 'Meta Description',
+ 'text' => 'Текст',
+ 'page_alias' => 'alias',
+ 'language_id' => 'ID мови',
+ 'common' => 'Загальне',
+ 'lang' => 'Мовні змінні',
+ 'termin' => 'Термін',
+ 'related' => 'Пов`язані',
+ 'menu' => 'Меню',
+ 'location' => 'Розташування',
+ 'book' => 'Справочник',
+
+ // Дима
+];
diff --git a/common/widgets/Alert.php b/common/widgets/Alert.php
new file mode 100644
index 0000000..8f1e590
--- /dev/null
+++ b/common/widgets/Alert.php
@@ -0,0 +1,79 @@
+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\bootstrap\Widget
+{
+ /**
+ * @var array the alert types configuration for the flash messages.
+ * This array is setup as $key => $value, where:
+ * - $key is the name of the session flash variable
+ * - $value is 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.
+ */
+ public $closeButton = [];
+
+ public function init()
+ {
+ parent::init();
+
+ $session = \Yii::$app->session;
+ $flashes = $session->getAllFlashes();
+ $appendCss = isset($this->options['class']) ? ' ' . $this->options['class'] : '';
+
+ foreach ($flashes as $type => $data) {
+ if (isset($this->alertTypes[$type])) {
+ $data = (array) $data;
+ foreach ($data as $i => $message) {
+ /* initialize css class for each alert box */
+ $this->options['class'] = $this->alertTypes[$type] . $appendCss;
+
+ /* assign unique id to each alert box */
+ $this->options['id'] = $this->getId() . '-' . $type . '-' . $i;
+
+ echo \yii\bootstrap\Alert::widget([
+ 'body' => $message,
+ 'closeButton' => $this->closeButton,
+ 'options' => $this->options,
+ ]);
+ }
+
+ $session->removeFlash($type);
+ }
+ }
+ }
+}
diff --git a/common/widgets/Multilang.php b/common/widgets/Multilang.php
new file mode 100644
index 0000000..98549ae
--- /dev/null
+++ b/common/widgets/Multilang.php
@@ -0,0 +1,45 @@
+id)) {
+ $this->id = \Yii::$app->security->generateRandomString(8);
+ }
+ if(empty($this->langs)) {
+ $this->langs = Language::getActiveLanguages();
+ }
+ if(empty($this->ajaxpath)) {
+ throw new InvalidParamException('ajaxpath must be set');
+ }
+ if(empty($this->form)) {
+ throw new InvalidParamException('form must be set');
+ }
+ ob_start();
+ echo $this->render('multilang-begin', ['id' => $this->id, 'langs' => $this->langs, 'data_langs' => $this->data_langs, 'ajaxpath' => $this->ajaxpath, 'form' => $this->form]);
+ }
+
+ public function run()
+ {
+ echo $this->render('multilang-end', ['id' => $this->id, 'langs' => $this->langs, 'data_langs' => $this->data_langs, 'ajaxpath' => $this->ajaxpath, 'form' => $this->form]);
+ $content = ob_get_clean();
+ return $content;
+ }
+}
\ No newline at end of file
diff --git a/common/widgets/Multilanguage.php b/common/widgets/Multilanguage.php
new file mode 100644
index 0000000..d701666
--- /dev/null
+++ b/common/widgets/Multilanguage.php
@@ -0,0 +1,87 @@
+default_lang = Language::getDefaultLang();
+ if(empty($this->langs)) {
+ $this->langs = Language::getActiveLanguages();
+ }
+ if(empty($this->form)) {
+ throw new InvalidParamException('Form must be set');
+ }
+ if(empty($this->ajaxView)) {
+ throw new InvalidParamException('Ajaxview must be set');
+ }
+ if(empty($this->data) || !is_array($this->data)) {
+ throw new InvalidParamException('Data must be set and be array');
+ } else {
+ $first = 1;
+ foreach ($this->data as $lang => $item) {
+ if ($first) {
+ $this->model_name = $item->className();
+ $this->table_name = $item->tableName();
+ $first = 0;
+ } else {
+ if($item->className() !== $this->model_name || $item->tableName() !== $this->table_name) {
+ throw new InvalidParamException('Every data element must have the same class and table');
+ }
+ }
+ }
+ }
+ }
+
+ public function run()
+ {
+ echo $this->render('multilanguage-begin', [
+ 'id' => $this->id,
+ 'model_name' => $this->model_name,
+ 'table_name' => $this->table_name,
+ 'data' => $this->data,
+ 'langs' => $this->langs,
+ 'handler' => $this->handler,
+ 'default_lang' => $this->default_lang,
+ 'ajaxView' => $this->ajaxView,
+ ]);
+ foreach($this->data as $lang => $item) {
+ $item->language_id = $lang;
+ echo $this->render($this->ajaxView, ['model' => $item, 'form' => $this->form, 'widget_id' => $this->id]);
+ }
+ echo $this->render('multilanguage-end', [
+ 'id' => $this->id,
+ 'model_name' => $this->model_name,
+ 'table_name' => $this->table_name,
+ 'data' => $this->data,
+ 'langs' => $this->langs,
+ 'handler' => $this->handler,
+ 'default_lang' => $this->default_lang,
+ 'ajaxView' => $this->ajaxView,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/common/widgets/views/multilang-begin.php b/common/widgets/views/multilang-begin.php
new file mode 100644
index 0000000..05205ee
--- /dev/null
+++ b/common/widgets/views/multilang-begin.php
@@ -0,0 +1,30 @@
+
+
+
+ = Yii::t('app', 'Add language') ?>
+
+
+
+
+
+ $data_lang) {
+ if(!$index) continue;
+ ?>
+
+
+
+
+
diff --git a/common/widgets/views/multilang-end.php b/common/widgets/views/multilang-end.php
new file mode 100644
index 0000000..fdec4e6
--- /dev/null
+++ b/common/widgets/views/multilang-end.php
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/common/widgets/views/multilanguage-begin.php b/common/widgets/views/multilanguage-begin.php
new file mode 100644
index 0000000..0b33e9d
--- /dev/null
+++ b/common/widgets/views/multilanguage-begin.php
@@ -0,0 +1,28 @@
+
+
+
+ = Yii::t('app', 'Add language') ?>
+
+
+
+
+
+ $data_lang) {
+ if(!$index) continue;
+ ?>
+
+
+
+
+
diff --git a/common/widgets/views/multilanguage-end.php b/common/widgets/views/multilanguage-end.php
new file mode 100644
index 0000000..6a6d5b9
--- /dev/null
+++ b/common/widgets/views/multilanguage-end.php
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..ca01b54
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,43 @@
+{
+ "name": "yiisoft/yii2-app-advanced",
+ "description": "Yii 2 Advanced Project Template",
+ "keywords": ["yii2", "framework", "advanced", "project template"],
+ "homepage": "http://www.yiiframework.com/",
+ "type": "project",
+ "license": "BSD-3-Clause",
+ "support": {
+ "issues": "https://github.com/yiisoft/yii2/issues?state=open",
+ "forum": "http://www.yiiframework.com/forum/",
+ "wiki": "http://www.yiiframework.com/wiki/",
+ "irc": "irc://irc.freenode.net/yii",
+ "source": "https://github.com/yiisoft/yii2"
+ },
+ "minimum-stability": "dev",
+ "require": {
+ "php": ">=5.4.0",
+ "yiisoft/yii2": ">=2.0.6",
+ "yiisoft/yii2-bootstrap": "*",
+ "yiisoft/yii2-swiftmailer": "*",
+ "dmstr/yii2-adminlte-asset": "2.*",
+ "yiisoft/yii2-jui": "^2.0",
+ "kartik-v/yii2-widget-select2": "@dev",
+ "mihaildev/yii2-ckeditor": "*",
+ "developeruz/yii2-db-rbac": "*",
+ "nodge/yii2-eauth": "*"
+ },
+ "require-dev": {
+ "yiisoft/yii2-codeception": "*",
+ "yiisoft/yii2-debug": "*",
+ "yiisoft/yii2-gii": "*",
+ "yiisoft/yii2-faker": "*"
+ },
+ "config": {
+ "process-timeout": 1800
+ },
+ "extra": {
+ "asset-installer-paths": {
+ "npm-asset-library": "vendor/npm",
+ "bower-asset-library": "vendor/bower"
+ }
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..efa182e
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,1575 @@
+{
+ "_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#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "hash": "0a46956b2ac16c603963c69e1b1f078c",
+ "content-hash": "1827ea8c78463126c172696034e7e2aa",
+ "packages": [
+ {
+ "name": "almasaeed2010/adminlte",
+ "version": "v2.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/almasaeed2010/AdminLTE.git",
+ "reference": "1ee281b3b99e8d8cccdc72fb8437c6888149cb46"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/almasaeed2010/AdminLTE/zipball/1ee281b3b99e8d8cccdc72fb8437c6888149cb46",
+ "reference": "1ee281b3b99e8d8cccdc72fb8437c6888149cb46",
+ "shasum": ""
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Abdullah Almsaeed",
+ "email": "support@almsaeedstudio.com"
+ }
+ ],
+ "description": "AdminLTE - admin control panel and dashboard that's based on Bootstrap 3",
+ "homepage": "http://almsaeedstudio.com/",
+ "keywords": [
+ "JS",
+ "admin",
+ "back-end",
+ "css",
+ "less",
+ "responsive",
+ "template",
+ "theme",
+ "web"
+ ],
+ "time": "2015-10-23 14:50:49"
+ },
+ {
+ "name": "bower-asset/bootstrap",
+ "version": "v3.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/twbs/bootstrap.git",
+ "reference": "16b48259a62f576e52c903c476bd42b90ab22482"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/twbs/bootstrap/zipball/16b48259a62f576e52c903c476bd42b90ab22482",
+ "reference": "16b48259a62f576e52c903c476bd42b90ab22482",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery": ">=1.9.1"
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": [
+ "less/bootstrap.less",
+ "dist/js/bootstrap.js"
+ ],
+ "bower-asset-ignore": [
+ "/.*",
+ "_config.yml",
+ "CNAME",
+ "composer.json",
+ "CONTRIBUTING.md",
+ "docs",
+ "js/tests",
+ "test-infra"
+ ]
+ },
+ "license": [
+ "MIT"
+ ],
+ "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
+ "keywords": [
+ "css",
+ "framework",
+ "front-end",
+ "js",
+ "less",
+ "mobile-first",
+ "responsive",
+ "web"
+ ]
+ },
+ {
+ "name": "bower-asset/fontawesome",
+ "version": "v4.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FortAwesome/Font-Awesome.git",
+ "reference": "fddd2c240452e6c8990c4ef75e0265b455aa7968"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FortAwesome/Font-Awesome/zipball/fddd2c240452e6c8990c4ef75e0265b455aa7968",
+ "reference": "fddd2c240452e6c8990c4ef75e0265b455aa7968",
+ "shasum": ""
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": [
+ "less/font-awesome.less",
+ "scss/font-awesome.scss"
+ ],
+ "bower-asset-ignore": [
+ "*/.*",
+ "*.json",
+ "src",
+ "*.yml",
+ "Gemfile",
+ "Gemfile.lock",
+ "*.md"
+ ]
+ },
+ "license": [
+ "OFL-1.1",
+ "MIT",
+ "CC-BY-3.0"
+ ],
+ "description": "Font Awesome"
+ },
+ {
+ "name": "bower-asset/jquery",
+ "version": "2.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jquery/jquery.git",
+ "reference": "7751e69b615c6eca6f783a81e292a55725af6b85"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jquery/jquery/zipball/7751e69b615c6eca6f783a81e292a55725af6b85",
+ "reference": "7751e69b615c6eca6f783a81e292a55725af6b85",
+ "shasum": ""
+ },
+ "require-dev": {
+ "bower-asset/qunit": "1.14.0",
+ "bower-asset/requirejs": "2.1.10",
+ "bower-asset/sinon": "1.8.1",
+ "bower-asset/sizzle": "2.1.1-patch2"
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": "dist/jquery.js",
+ "bower-asset-ignore": [
+ "**/.*",
+ "build",
+ "dist/cdn",
+ "speed",
+ "test",
+ "*.md",
+ "AUTHORS.txt",
+ "Gruntfile.js",
+ "package.json"
+ ]
+ },
+ "license": [
+ "MIT"
+ ],
+ "keywords": [
+ "javascript",
+ "jquery",
+ "library"
+ ]
+ },
+ {
+ "name": "bower-asset/jquery-ui",
+ "version": "1.11.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/components/jqueryui.git",
+ "reference": "c34f8dbf3ba57b3784b93f26119f436c0e8288e1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/components/jqueryui/zipball/c34f8dbf3ba57b3784b93f26119f436c0e8288e1",
+ "reference": "c34f8dbf3ba57b3784b93f26119f436c0e8288e1",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery": ">=1.6"
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": [
+ "jquery-ui.js"
+ ],
+ "bower-asset-ignore": []
+ }
+ },
+ {
+ "name": "bower-asset/jquery.inputmask",
+ "version": "3.1.63",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/RobinHerbots/jquery.inputmask.git",
+ "reference": "c40c7287eadc31e341ebbf0c02352eb55b9cbc48"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/c40c7287eadc31e341ebbf0c02352eb55b9cbc48",
+ "reference": "c40c7287eadc31e341ebbf0c02352eb55b9cbc48",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery": ">=1.7"
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": [
+ "./dist/inputmask/jquery.inputmask.js",
+ "./dist/inputmask/jquery.inputmask.extensions.js",
+ "./dist/inputmask/jquery.inputmask.date.extensions.js",
+ "./dist/inputmask/jquery.inputmask.numeric.extensions.js",
+ "./dist/inputmask/jquery.inputmask.phone.extensions.js",
+ "./dist/inputmask/jquery.inputmask.regex.extensions.js"
+ ],
+ "bower-asset-ignore": [
+ "**/.*",
+ "qunit/",
+ "nuget/",
+ "tools/",
+ "js/",
+ "*.md",
+ "build.properties",
+ "build.xml",
+ "jquery.inputmask.jquery.json"
+ ]
+ },
+ "license": [
+ "http://opensource.org/licenses/mit-license.php"
+ ],
+ "description": "jquery.inputmask is a jquery plugin which create an input mask.",
+ "keywords": [
+ "form",
+ "input",
+ "inputmask",
+ "jquery",
+ "mask",
+ "plugins"
+ ]
+ },
+ {
+ "name": "bower-asset/punycode",
+ "version": "v1.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bestiejs/punycode.js.git",
+ "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bestiejs/punycode.js/zipball/38c8d3131a82567bfef18da09f7f4db68c84f8a3",
+ "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3",
+ "shasum": ""
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": "punycode.js",
+ "bower-asset-ignore": [
+ "coverage",
+ "tests",
+ ".*",
+ "component.json",
+ "Gruntfile.js",
+ "node_modules",
+ "package.json"
+ ]
+ }
+ },
+ {
+ "name": "bower-asset/yii2-pjax",
+ "version": "v2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/jquery-pjax.git",
+ "reference": "6818718408086db6bdcf33649cecb86b6b4f9b67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/6818718408086db6bdcf33649cecb86b6b4f9b67",
+ "reference": "6818718408086db6bdcf33649cecb86b6b4f9b67",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery": ">=1.8"
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": "./jquery.pjax.js",
+ "bower-asset-ignore": [
+ ".travis.yml",
+ "Gemfile",
+ "Gemfile.lock",
+ "vendor/",
+ "script/",
+ "test/"
+ ]
+ },
+ "license": [
+ "MIT"
+ ]
+ },
+ {
+ "name": "cebe/markdown",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cebe/markdown.git",
+ "reference": "54a2c49de31cc44e864ebf0500a35ef21d0010b2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cebe/markdown/zipball/54a2c49de31cc44e864ebf0500a35ef21d0010b2",
+ "reference": "54a2c49de31cc44e864ebf0500a35ef21d0010b2",
+ "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.1.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"
+ ],
+ "time": "2015-03-06 05:28:07"
+ },
+ {
+ "name": "cebe/yii2-gravatar",
+ "version": "1.1",
+ "target-dir": "cebe/gravatar",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cebe/yii2-gravatar.git",
+ "reference": "c9c01bd14c9bdee9e5ae1ef1aad23f80c182c057"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cebe/yii2-gravatar/zipball/c9c01bd14c9bdee9e5ae1ef1aad23f80c182c057",
+ "reference": "c9c01bd14c9bdee9e5ae1ef1aad23f80c182c057",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2": "*"
+ },
+ "type": "yii2-extension",
+ "autoload": {
+ "psr-0": {
+ "cebe\\gravatar\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Carsten Brandt",
+ "email": "mail@cebe.cc",
+ "homepage": "http://cebe.cc/",
+ "role": "Core framework development"
+ }
+ ],
+ "description": "Gravatar Widget for Yii 2",
+ "keywords": [
+ "gravatar",
+ "yii"
+ ],
+ "time": "2013-12-10 17:49:58"
+ },
+ {
+ "name": "developeruz/yii2-db-rbac",
+ "version": "1.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/developeruz/yii2-db-rbac.git",
+ "reference": "28c1b0ebcc45b6365af6f1e9949b4d9cfeaebf1b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/developeruz/yii2-db-rbac/zipball/28c1b0ebcc45b6365af6f1e9949b4d9cfeaebf1b",
+ "reference": "28c1b0ebcc45b6365af6f1e9949b4d9cfeaebf1b",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2": "*"
+ },
+ "type": "yii2-extension",
+ "autoload": {
+ "psr-4": {
+ "developeruz\\db_rbac\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Elvira Sheina",
+ "email": "elleuz@gmail.com",
+ "homepage": "http://developer.uz"
+ }
+ ],
+ "description": "Dynamic control of access rights in YII2",
+ "keywords": [
+ "rbac",
+ "yii"
+ ],
+ "time": "2015-10-03 05:56:47"
+ },
+ {
+ "name": "dmstr/yii2-adminlte-asset",
+ "version": "2.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dmstr/yii2-adminlte-asset.git",
+ "reference": "c842a15ceef4e903f70ac927ec3246e6d53e1148"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dmstr/yii2-adminlte-asset/zipball/c842a15ceef4e903f70ac927ec3246e6d53e1148",
+ "reference": "c842a15ceef4e903f70ac927ec3246e6d53e1148",
+ "shasum": ""
+ },
+ "require": {
+ "almasaeed2010/adminlte": "~2.0",
+ "cebe/yii2-gravatar": "1.*",
+ "rmrevin/yii2-fontawesome": "~2.9",
+ "yiisoft/yii2": "2.*",
+ "yiisoft/yii2-bootstrap": "2.*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "dmstr\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Tobias Munk",
+ "email": "tobias@diemeisterei.de"
+ },
+ {
+ "name": "Evgeniy Tkachenko",
+ "email": "et.coder@gmail.com"
+ }
+ ],
+ "description": "Backend theme for Yii2 Framework",
+ "keywords": [
+ "AdminLTE",
+ "extension",
+ "yii2"
+ ],
+ "time": "2015-11-06 10:35:36"
+ },
+ {
+ "name": "ezyang/htmlpurifier",
+ "version": "v4.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ezyang/htmlpurifier.git",
+ "reference": "6f389f0f25b90d0b495308efcfa073981177f0fd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/6f389f0f25b90d0b495308efcfa073981177f0fd",
+ "reference": "6f389f0f25b90d0b495308efcfa073981177f0fd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "HTMLPurifier": "library/"
+ },
+ "files": [
+ "library/HTMLPurifier.composer.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL"
+ ],
+ "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"
+ ],
+ "time": "2013-11-30 08:25:19"
+ },
+ {
+ "name": "kartik-v/yii2-krajee-base",
+ "version": "v1.7.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kartik-v/yii2-krajee-base.git",
+ "reference": "6f10fd0a0bfccd729764c65fa65eb4ccf2cbade9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kartik-v/yii2-krajee-base/zipball/6f10fd0a0bfccd729764c65fa65eb4ccf2cbade9",
+ "reference": "6f10fd0a0bfccd729764c65fa65eb4ccf2cbade9",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2-bootstrap": "@dev"
+ },
+ "type": "yii2-extension",
+ "autoload": {
+ "psr-4": {
+ "kartik\\base\\": ""
+ }
+ },
+ "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"
+ ],
+ "time": "2015-11-25 07:03:35"
+ },
+ {
+ "name": "kartik-v/yii2-widget-select2",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kartik-v/yii2-widget-select2.git",
+ "reference": "fa0175d936012eac3f7a382792cbb13948015c4e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kartik-v/yii2-widget-select2/zipball/fa0175d936012eac3f7a382792cbb13948015c4e",
+ "reference": "fa0175d936012eac3f7a382792cbb13948015c4e",
+ "shasum": ""
+ },
+ "require": {
+ "kartik-v/yii2-krajee-base": "~1.7"
+ },
+ "type": "yii2-extension",
+ "autoload": {
+ "psr-4": {
+ "kartik\\select2\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Kartik Visweswaran",
+ "email": "kartikv2@gmail.com",
+ "homepage": "http://www.krajee.com/"
+ }
+ ],
+ "description": "Enhanced Yii2 wrapper for the Select2 jQuery plugin (sub repo split from yii2-widgets).",
+ "homepage": "https://github.com/kartik-v/yii2-widget-select2",
+ "keywords": [
+ "dropdown",
+ "extension",
+ "form",
+ "jquery",
+ "plugin",
+ "select2",
+ "widget",
+ "yii2"
+ ],
+ "time": "2015-09-22 03:10:57"
+ },
+ {
+ "name": "lusitanian/oauth",
+ "version": "v0.3.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Lusitanian/PHPoAuthLib.git",
+ "reference": "4ce8c488971410233eb3b1e6d9ac4e81debb41d5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Lusitanian/PHPoAuthLib/zipball/4ce8c488971410233eb3b1e6d9ac4e81debb41d5",
+ "reference": "4ce8c488971410233eb3b1e6d9ac4e81debb41d5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "3.7.*",
+ "predis/predis": "0.8.*@dev",
+ "symfony/http-foundation": "~2.1"
+ },
+ "suggest": {
+ "ext-openssl": "Allows for usage of secure connections with the stream-based HTTP client.",
+ "predis/predis": "Allows using the Redis storage backend.",
+ "symfony/http-foundation": "Allows using the Symfony Session storage backend."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "OAuth": "src",
+ "OAuth\\Unit": "tests"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "David Desberg",
+ "email": "david@daviddesberg.com"
+ },
+ {
+ "name": "Pieter Hordijk",
+ "email": "info@pieterhordijk.com"
+ }
+ ],
+ "description": "PHP 5.3+ oAuth 1/2 Library",
+ "keywords": [
+ "Authentication",
+ "authorization",
+ "oauth",
+ "security"
+ ],
+ "time": "2015-09-09 06:43:02"
+ },
+ {
+ "name": "mihaildev/yii2-ckeditor",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MihailDev/yii2-ckeditor.git",
+ "reference": "d20aa7f6bcf610fee226d6eb15212a279875bf87"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MihailDev/yii2-ckeditor/zipball/d20aa7f6bcf610fee226d6eb15212a279875bf87",
+ "reference": "d20aa7f6bcf610fee226d6eb15212a279875bf87",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2": "*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "asset-installer-paths": {
+ "npm-asset-library": "vendor/npm",
+ "bower-asset-library": "vendor/bower"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "mihaildev\\ckeditor\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Mihail",
+ "email": "mihail.kucher@gmail.com",
+ "homepage": "https://github.com/MihailDev",
+ "role": "Developer"
+ }
+ ],
+ "description": "Yii2 CKEditor",
+ "homepage": "https://github.com/MihailDev/yii2-ckeditor",
+ "keywords": [
+ "CKEditor",
+ "editor",
+ "wysiwyg",
+ "yii"
+ ],
+ "time": "2014-11-19 22:04:08"
+ },
+ {
+ "name": "nodge/lightopenid",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Nodge/LightOpenID.git",
+ "reference": "a5492cc0c932c557b7e9b54a6e5bbd85cc5fa041"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Nodge/LightOpenID/zipball/a5492cc0c932c557b7e9b54a6e5bbd85cc5fa041",
+ "reference": "a5492cc0c932c557b7e9b54a6e5bbd85cc5fa041",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "openid.php",
+ "provider/provider.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT License"
+ ],
+ "authors": [
+ {
+ "name": "Mewp",
+ "homepage": "http://code.google.com/p/lightopenid/"
+ },
+ {
+ "name": "Ignat Ignatov",
+ "homepage": "https://github.com/iignatov/LightOpenID"
+ }
+ ],
+ "description": "Lightweight PHP5 library for easy OpenID authentication.",
+ "homepage": "https://github.com/Nodge/LightOpenID",
+ "keywords": [
+ "Authentication",
+ "OpenId"
+ ],
+ "time": "2013-08-31 16:48:56"
+ },
+ {
+ "name": "nodge/yii2-eauth",
+ "version": "2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Nodge/yii2-eauth.git",
+ "reference": "25963e78c6083734736187bff992dd9da60fd125"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Nodge/yii2-eauth/zipball/25963e78c6083734736187bff992dd9da60fd125",
+ "reference": "25963e78c6083734736187bff992dd9da60fd125",
+ "shasum": ""
+ },
+ "require": {
+ "lib-curl": "*",
+ "lusitanian/oauth": "~0.3.0",
+ "nodge/lightopenid": "~1.1.0",
+ "php": ">=5.4.0",
+ "yiisoft/yii2": "*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "bootstrap": "nodge\\eauth\\Bootstrap"
+ },
+ "autoload": {
+ "psr-4": {
+ "nodge\\eauth\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "New BSD License"
+ ],
+ "authors": [
+ {
+ "name": "Maxim Zemskov",
+ "email": "nodge@yandex.ru",
+ "homepage": "http://nodge.ru/"
+ }
+ ],
+ "description": "Yii2 EAuth Extension. EAuth allows to authenticate users with accounts on other websites (Google, Facebook, Twitter, etc).",
+ "homepage": "https://github.com/Nodge/yii2-eauth",
+ "keywords": [
+ "Authentication",
+ "OpenId",
+ "eauth",
+ "extension",
+ "oauth",
+ "yii2"
+ ],
+ "time": "2015-10-17 16:51:17"
+ },
+ {
+ "name": "rmrevin/yii2-fontawesome",
+ "version": "2.13.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/rmrevin/yii2-fontawesome.git",
+ "reference": "2efbfacb22be59f373d11a7e3dfa9213e2ba18a9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/rmrevin/yii2-fontawesome/zipball/2efbfacb22be59f373d11a7e3dfa9213e2ba18a9",
+ "reference": "2efbfacb22be59f373d11a7e3dfa9213e2ba18a9",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/fontawesome": "4.5.*",
+ "php": ">=5.4.0",
+ "yiisoft/yii2": "2.0.*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "asset-installer-paths": {
+ "npm-asset-library": "vendor/npm",
+ "bower-asset-library": "vendor/bower"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "rmrevin\\yii\\fontawesome\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Revin Roman",
+ "email": "roman@rmrevin.com",
+ "homepage": "https://rmrevin.com/"
+ }
+ ],
+ "description": "Asset Bundle for Yii2 with Font Awesome",
+ "keywords": [
+ "asset",
+ "awesome",
+ "bundle",
+ "font",
+ "yii"
+ ],
+ "time": "2015-11-26 15:24:53"
+ },
+ {
+ "name": "swiftmailer/swiftmailer",
+ "version": "v5.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/swiftmailer/swiftmailer.git",
+ "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0697e6aa65c83edf97bb0f23d8763f94e3f11421",
+ "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "mockery/mockery": "~0.9.1,<0.9.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.4-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "lib/swift_required.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Corbyn"
+ },
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "Swiftmailer, free feature-rich PHP mailer",
+ "homepage": "http://swiftmailer.org",
+ "keywords": [
+ "email",
+ "mail",
+ "mailer"
+ ],
+ "time": "2015-06-06 14:19:39"
+ },
+ {
+ "name": "yiisoft/yii2",
+ "version": "2.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-framework.git",
+ "reference": "f42b2eb80f61992438661b01d0d74c6738e2ff38"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/f42b2eb80f61992438661b01d0d74c6738e2ff38",
+ "reference": "f42b2eb80f61992438661b01d0d74c6738e2ff38",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery": "2.1.*@stable | 1.11.*@stable",
+ "bower-asset/jquery.inputmask": "3.1.*",
+ "bower-asset/punycode": "1.3.*",
+ "bower-asset/yii2-pjax": ">=2.0.1",
+ "cebe/markdown": "~1.0.0 | ~1.1.0",
+ "ext-mbstring": "*",
+ "ezyang/htmlpurifier": "4.6.*",
+ "lib-pcre": "*",
+ "php": ">=5.4.0",
+ "yiisoft/yii2-composer": "*"
+ },
+ "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": "http://www.yiiframework.com/",
+ "role": "Founder and project lead"
+ },
+ {
+ "name": "Alexander Makarov",
+ "email": "sam@rmcreative.ru",
+ "homepage": "http://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": "http://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"
+ }
+ ],
+ "description": "Yii PHP Framework Version 2",
+ "homepage": "http://www.yiiframework.com/",
+ "keywords": [
+ "framework",
+ "yii2"
+ ],
+ "time": "2015-08-05 22:00:30"
+ },
+ {
+ "name": "yiisoft/yii2-bootstrap",
+ "version": "2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-bootstrap.git",
+ "reference": "1464f93834b1d5edb1f5625f7ffd6c3723fa4923"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-bootstrap/zipball/1464f93834b1d5edb1f5625f7ffd6c3723fa4923",
+ "reference": "1464f93834b1d5edb1f5625f7ffd6c3723fa4923",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/bootstrap": "3.3.* | 3.2.* | 3.1.*",
+ "yiisoft/yii2": ">=2.0.6"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ },
+ "asset-installer-paths": {
+ "npm-asset-library": "vendor/npm",
+ "bower-asset-library": "vendor/bower"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\bootstrap\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Qiang Xue",
+ "email": "qiang.xue@gmail.com"
+ }
+ ],
+ "description": "The Twitter Bootstrap extension for the Yii framework",
+ "keywords": [
+ "bootstrap",
+ "yii2"
+ ],
+ "time": "2015-09-23 17:48:24"
+ },
+ {
+ "name": "yiisoft/yii2-composer",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-composer.git",
+ "reference": "ca8d23707ae47d20b0454e4b135c156f6da6d7be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/ca8d23707ae47d20b0454e4b135c156f6da6d7be",
+ "reference": "ca8d23707ae47d20b0454e4b135c156f6da6d7be",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "1.0.0"
+ },
+ "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"
+ }
+ ],
+ "description": "The composer plugin for Yii extension installer",
+ "keywords": [
+ "composer",
+ "extension installer",
+ "yii2"
+ ],
+ "time": "2015-03-01 06:22:44"
+ },
+ {
+ "name": "yiisoft/yii2-jui",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-jui.git",
+ "reference": "ce16f29cca654702c2ee500ccad9657a2d2e6a09"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-jui/zipball/ce16f29cca654702c2ee500ccad9657a2d2e6a09",
+ "reference": "ce16f29cca654702c2ee500ccad9657a2d2e6a09",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery-ui": "1.11.*@stable",
+ "yiisoft/yii2": ">=2.0.4"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ },
+ "asset-installer-paths": {
+ "npm-asset-library": "vendor/npm",
+ "bower-asset-library": "vendor/bower"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\jui\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Qiang Xue",
+ "email": "qiang.xue@gmail.com"
+ }
+ ],
+ "description": "The Jquery UI extension for the Yii framework",
+ "keywords": [
+ "jQuery UI",
+ "yii2"
+ ],
+ "time": "2015-05-10 22:09:43"
+ },
+ {
+ "name": "yiisoft/yii2-swiftmailer",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-swiftmailer.git",
+ "reference": "4ec435a89e30b203cea99770910fb5499cb3627a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-swiftmailer/zipball/4ec435a89e30b203cea99770910fb5499cb3627a",
+ "reference": "4ec435a89e30b203cea99770910fb5499cb3627a",
+ "shasum": ""
+ },
+ "require": {
+ "swiftmailer/swiftmailer": "~5.0",
+ "yiisoft/yii2": ">=2.0.4"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\swiftmailer\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Paul Klimov",
+ "email": "klimov.paul@gmail.com"
+ }
+ ],
+ "description": "The SwiftMailer integration for the Yii framework",
+ "keywords": [
+ "email",
+ "mail",
+ "mailer",
+ "swift",
+ "swiftmailer",
+ "yii2"
+ ],
+ "time": "2015-05-10 22:12:32"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "bower-asset/typeahead.js",
+ "version": "v0.10.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/twitter/typeahead.js.git",
+ "reference": "5f198b87d1af845da502ea9df93a5e84801ce742"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/twitter/typeahead.js/zipball/5f198b87d1af845da502ea9df93a5e84801ce742",
+ "reference": "5f198b87d1af845da502ea9df93a5e84801ce742",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery": ">=1.7"
+ },
+ "require-dev": {
+ "bower-asset/jasmine-ajax": "~1.3.1",
+ "bower-asset/jasmine-jquery": "~1.5.2",
+ "bower-asset/jquery": "~1.7"
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": "dist/typeahead.bundle.js"
+ }
+ },
+ {
+ "name": "fzaninotto/faker",
+ "version": "v1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fzaninotto/Faker.git",
+ "reference": "d0190b156bcca848d401fb80f31f504f37141c8d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d0190b156bcca848d401fb80f31f504f37141c8d",
+ "reference": "d0190b156bcca848d401fb80f31f504f37141c8d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0",
+ "squizlabs/php_codesniffer": "~1.5"
+ },
+ "suggest": {
+ "ext-intl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5.x-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"
+ ],
+ "time": "2015-05-29 06:29:14"
+ },
+ {
+ "name": "phpspec/php-diff",
+ "version": "v1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpspec/php-diff.git",
+ "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpspec/php-diff/zipball/30e103d19519fe678ae64a60d77884ef3d71b28a",
+ "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a",
+ "shasum": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Diff": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Chris Boulton",
+ "homepage": "http://github.com/chrisboulton",
+ "role": "Original developer"
+ }
+ ],
+ "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).",
+ "time": "2013-11-01 13:02:21"
+ },
+ {
+ "name": "yiisoft/yii2-codeception",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-codeception.git",
+ "reference": "de5007e7a99359597abbfe1c88dca3ce620061c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-codeception/zipball/de5007e7a99359597abbfe1c88dca3ce620061c5",
+ "reference": "de5007e7a99359597abbfe1c88dca3ce620061c5",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2": ">=2.0.4"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\codeception\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Mark Jebri",
+ "email": "mark.github@yandex.ru"
+ }
+ ],
+ "description": "The Codeception integration for the Yii framework",
+ "keywords": [
+ "codeception",
+ "yii2"
+ ],
+ "time": "2015-05-10 22:08:30"
+ },
+ {
+ "name": "yiisoft/yii2-debug",
+ "version": "2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-debug.git",
+ "reference": "1b302e67521d46feb2413d9d96ca94ed82b39b0e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-debug/zipball/1b302e67521d46feb2413d9d96ca94ed82b39b0e",
+ "reference": "1b302e67521d46feb2413d9d96ca94ed82b39b0e",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2": ">=2.0.4",
+ "yiisoft/yii2-bootstrap": "*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\debug\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Qiang Xue",
+ "email": "qiang.xue@gmail.com"
+ }
+ ],
+ "description": "The debugger extension for the Yii framework",
+ "keywords": [
+ "debug",
+ "debugger",
+ "yii2"
+ ],
+ "time": "2015-08-06 16:14:06"
+ },
+ {
+ "name": "yiisoft/yii2-faker",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-faker.git",
+ "reference": "b88ca69ee226a3610b2c26c026c3203d7ac50f6c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-faker/zipball/b88ca69ee226a3610b2c26c026c3203d7ac50f6c",
+ "reference": "b88ca69ee226a3610b2c26c026c3203d7ac50f6c",
+ "shasum": ""
+ },
+ "require": {
+ "fzaninotto/faker": "*",
+ "yiisoft/yii2": "*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\faker\\": ""
+ }
+ },
+ "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"
+ ],
+ "time": "2015-03-01 06:22:44"
+ },
+ {
+ "name": "yiisoft/yii2-gii",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-gii.git",
+ "reference": "e5a023e8779bd774194842ec1b8fb4917cf04007"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-gii/zipball/e5a023e8779bd774194842ec1b8fb4917cf04007",
+ "reference": "e5a023e8779bd774194842ec1b8fb4917cf04007",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/typeahead.js": "0.10.*",
+ "phpspec/php-diff": ">=1.0.2",
+ "yiisoft/yii2": ">=2.0.4",
+ "yiisoft/yii2-bootstrap": "~2.0"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ },
+ "asset-installer-paths": {
+ "npm-asset-library": "vendor/npm",
+ "bower-asset-library": "vendor/bower"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\gii\\": ""
+ }
+ },
+ "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"
+ ],
+ "time": "2015-05-10 22:09:31"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {
+ "kartik-v/yii2-widget-select2": 20
+ },
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=5.4.0"
+ },
+ "platform-dev": []
+}
diff --git a/console/config/.gitignore b/console/config/.gitignore
new file mode 100644
index 0000000..20da318
--- /dev/null
+++ b/console/config/.gitignore
@@ -0,0 +1,2 @@
+main-local.php
+params-local.php
\ No newline at end of file
diff --git a/console/config/bootstrap.php b/console/config/bootstrap.php
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/console/config/bootstrap.php
@@ -0,0 +1 @@
+ 'app-console',
+ 'basePath' => dirname(__DIR__),
+ 'bootstrap' => ['log'],
+ 'controllerNamespace' => 'console\controllers',
+ 'components' => [
+ 'log' => [
+ 'targets' => [
+ [
+ 'class' => 'yii\log\FileTarget',
+ 'levels' => ['error', 'warning'],
+ ],
+ ],
+ ],
+ ],
+ 'params' => $params,
+];
diff --git a/console/config/params.php b/console/config/params.php
new file mode 100644
index 0000000..7f754b9
--- /dev/null
+++ b/console/config/params.php
@@ -0,0 +1,4 @@
+ 'admin@example.com',
+];
diff --git a/console/controllers/.gitkeep b/console/controllers/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/console/controllers/.gitkeep
diff --git a/console/migrations/m130524_201442_init.php b/console/migrations/m130524_201442_init.php
new file mode 100644
index 0000000..81a322a
--- /dev/null
+++ b/console/migrations/m130524_201442_init.php
@@ -0,0 +1,34 @@
+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/models/.gitkeep b/console/models/.gitkeep
new file mode 100644
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 100644
index 0000000..c96a04f
--- /dev/null
+++ b/console/runtime/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/db-migration/andryeyev/all.backup b/db-migration/andryeyev/all.backup
new file mode 100644
index 0000000..dc753e9
Binary files /dev/null and b/db-migration/andryeyev/all.backup differ
diff --git a/db-migration/andryeyev/menu.backup b/db-migration/andryeyev/menu.backup
new file mode 100644
index 0000000..6069452
Binary files /dev/null and b/db-migration/andryeyev/menu.backup differ
diff --git a/db-migration/andryeyev/termin.backup b/db-migration/andryeyev/termin.backup
new file mode 100644
index 0000000..d81612b
Binary files /dev/null and b/db-migration/andryeyev/termin.backup differ
diff --git a/db-migration/artbox/artbox_db.backup b/db-migration/artbox/artbox_db.backup
new file mode 100644
index 0000000..00767f3
Binary files /dev/null and b/db-migration/artbox/artbox_db.backup differ
diff --git a/db-migration/dmitryi/all.backup b/db-migration/dmitryi/all.backup
new file mode 100644
index 0000000..a6de240
Binary files /dev/null and b/db-migration/dmitryi/all.backup differ
diff --git a/db-migration/dmitryi/artbox_db.backup b/db-migration/dmitryi/artbox_db.backup
new file mode 100644
index 0000000..c9592fb
Binary files /dev/null and b/db-migration/dmitryi/artbox_db.backup differ
diff --git a/db-migration/dmitryi/auth.backup b/db-migration/dmitryi/auth.backup
new file mode 100644
index 0000000..40c5854
Binary files /dev/null and b/db-migration/dmitryi/auth.backup differ
diff --git a/db-migration/dmitryi/social.backup b/db-migration/dmitryi/social.backup
new file mode 100644
index 0000000..a4bc4db
Binary files /dev/null and b/db-migration/dmitryi/social.backup differ
diff --git a/db-migration/dmitryi/user.backup b/db-migration/dmitryi/user.backup
new file mode 100644
index 0000000..a1ded00
Binary files /dev/null and b/db-migration/dmitryi/user.backup differ
diff --git a/db-migration/pubic.backup b/db-migration/pubic.backup
new file mode 100644
index 0000000..5c92ed5
Binary files /dev/null and b/db-migration/pubic.backup differ
diff --git a/db-migration/pubic.sql b/db-migration/pubic.sql
new file mode 100644
index 0000000..30b9a20
--- /dev/null
+++ b/db-migration/pubic.sql
@@ -0,0 +1,4268 @@
+--
+-- PostgreSQL database dump
+--
+
+-- Dumped from database version 9.4.4
+-- Dumped by pg_dump version 9.4.4
+-- Started on 2016-01-15 15:34:47
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SET check_function_bodies = false;
+SET client_min_messages = warning;
+
+--
+-- TOC entry 222 (class 3079 OID 11855)
+-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
+--
+
+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
+
+
+--
+-- TOC entry 2597 (class 0 OID 0)
+-- Dependencies: 222
+-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
+--
+
+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
+
+
+--
+-- TOC entry 224 (class 3079 OID 17321)
+-- Name: hstore; Type: EXTENSION; Schema: -; Owner:
+--
+
+CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA public;
+
+
+--
+-- TOC entry 2598 (class 0 OID 0)
+-- Dependencies: 224
+-- Name: EXTENSION hstore; Type: COMMENT; Schema: -; Owner:
+--
+
+COMMENT ON EXTENSION hstore IS 'data type for storing sets of (key, value) pairs';
+
+
+--
+-- TOC entry 223 (class 3079 OID 17444)
+-- Name: intarray; Type: EXTENSION; Schema: -; Owner:
+--
+
+CREATE EXTENSION IF NOT EXISTS intarray WITH SCHEMA public;
+
+
+--
+-- TOC entry 2599 (class 0 OID 0)
+-- Dependencies: 223
+-- Name: EXTENSION intarray; Type: COMMENT; Schema: -; Owner:
+--
+
+COMMENT ON EXTENSION intarray IS 'functions, operators, and index support for 1-D arrays of integers';
+
+
+SET search_path = public, pg_catalog;
+
+SET default_tablespace = '';
+
+SET default_with_oids = false;
+
+--
+-- TOC entry 191 (class 1259 OID 83922)
+-- Name: admin_menu; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE admin_menu (
+ admin_menu_id integer NOT NULL,
+ admin_menu_pid integer,
+ status smallint DEFAULT 0 NOT NULL,
+ hide_min smallint DEFAULT 0 NOT NULL,
+ sort integer,
+ name character varying NOT NULL,
+ path character varying,
+ param character varying
+);
+
+
+ALTER TABLE admin_menu OWNER TO postgres;
+
+--
+-- TOC entry 192 (class 1259 OID 83930)
+-- Name: admin_menu_access_group; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE admin_menu_access_group (
+ admin_menu_access_group_id integer NOT NULL,
+ admin_menu_id integer NOT NULL,
+ "group" character varying NOT NULL
+);
+
+
+ALTER TABLE admin_menu_access_group OWNER TO postgres;
+
+--
+-- TOC entry 193 (class 1259 OID 83936)
+-- Name: admin_menu_access_group_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE admin_menu_access_group_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE admin_menu_access_group_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2600 (class 0 OID 0)
+-- Dependencies: 193
+-- Name: admin_menu_access_group_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE admin_menu_access_group_id_seq OWNED BY admin_menu_access_group.admin_menu_access_group_id;
+
+
+--
+-- TOC entry 194 (class 1259 OID 83938)
+-- Name: admin_menu_access_user; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE admin_menu_access_user (
+ admin_menu_id integer NOT NULL,
+ user_id integer NOT NULL,
+ admin_menu_access_user_id integer NOT NULL
+);
+
+
+ALTER TABLE admin_menu_access_user OWNER TO postgres;
+
+--
+-- TOC entry 195 (class 1259 OID 83941)
+-- Name: admin_menu_access_user_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE admin_menu_access_user_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE admin_menu_access_user_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2601 (class 0 OID 0)
+-- Dependencies: 195
+-- Name: admin_menu_access_user_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE admin_menu_access_user_id_seq OWNED BY admin_menu_access_user.admin_menu_access_user_id;
+
+
+--
+-- TOC entry 196 (class 1259 OID 83943)
+-- Name: admin_menu_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE admin_menu_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE admin_menu_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2602 (class 0 OID 0)
+-- Dependencies: 196
+-- Name: admin_menu_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE admin_menu_id_seq OWNED BY admin_menu.admin_menu_id;
+
+
+--
+-- TOC entry 203 (class 1259 OID 84041)
+-- Name: article; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE article (
+ article_id integer NOT NULL,
+ sort integer DEFAULT 100 NOT NULL,
+ date_add timestamp without time zone DEFAULT now() NOT NULL,
+ date_update timestamp without time zone DEFAULT now() NOT NULL,
+ code character varying NOT NULL,
+ user_id integer NOT NULL,
+ tag character varying,
+ article_pid integer,
+ status smallint DEFAULT 0 NOT NULL,
+ comment smallint DEFAULT (0)::smallint NOT NULL,
+ vote smallint DEFAULT (0)::smallint NOT NULL
+);
+
+
+ALTER TABLE article OWNER TO postgres;
+
+--
+-- TOC entry 204 (class 1259 OID 84054)
+-- Name: article_category; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE article_category (
+ article_category_id integer NOT NULL,
+ sort integer DEFAULT 100 NOT NULL,
+ code character varying NOT NULL,
+ date_add time without time zone DEFAULT now() NOT NULL,
+ date_update time without time zone DEFAULT now() NOT NULL,
+ tag character varying,
+ article_category_pid integer,
+ status smallint DEFAULT 0 NOT NULL
+);
+
+
+ALTER TABLE article_category OWNER TO postgres;
+
+--
+-- TOC entry 213 (class 1259 OID 84273)
+-- Name: article_category_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE article_category_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE article_category_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2603 (class 0 OID 0)
+-- Dependencies: 213
+-- Name: article_category_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE article_category_id_seq OWNED BY article_category.article_category_id;
+
+
+--
+-- TOC entry 205 (class 1259 OID 84064)
+-- Name: article_category_lang; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE article_category_lang (
+ article_category_lang_id integer NOT NULL,
+ language_id integer DEFAULT 0 NOT NULL,
+ article_category_id integer,
+ text text NOT NULL,
+ preview text,
+ seo_url character varying,
+ name character varying NOT NULL,
+ meta_title character varying,
+ meta_descr text,
+ meta_keyword character varying,
+ h1_tag character varying,
+ tag character varying
+);
+
+
+ALTER TABLE article_category_lang OWNER TO postgres;
+
+--
+-- TOC entry 215 (class 1259 OID 84279)
+-- Name: article_category_lang_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE article_category_lang_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE article_category_lang_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2604 (class 0 OID 0)
+-- Dependencies: 215
+-- Name: article_category_lang_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE article_category_lang_id_seq OWNED BY article_category_lang.article_category_lang_id;
+
+
+--
+-- TOC entry 206 (class 1259 OID 84071)
+-- Name: article_category_media; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE article_category_media (
+ article_category_media_id integer NOT NULL,
+ article_category_id integer NOT NULL,
+ media_id integer NOT NULL,
+ media_alt character varying,
+ media_title character varying,
+ media_caption character varying,
+ type character varying(10) DEFAULT 'additional'::character varying NOT NULL,
+ language_id integer
+);
+
+
+ALTER TABLE article_category_media OWNER TO postgres;
+
+--
+-- TOC entry 216 (class 1259 OID 84282)
+-- Name: article_category_media_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE article_category_media_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE article_category_media_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2605 (class 0 OID 0)
+-- Dependencies: 216
+-- Name: article_category_media_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE article_category_media_id_seq OWNED BY article_category_media.article_category_media_id;
+
+
+--
+-- TOC entry 214 (class 1259 OID 84276)
+-- Name: article_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE article_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE article_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2606 (class 0 OID 0)
+-- Dependencies: 214
+-- Name: article_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE article_id_seq OWNED BY article.article_id;
+
+
+--
+-- TOC entry 207 (class 1259 OID 84078)
+-- Name: article_lang; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE article_lang (
+ article_lang_id integer NOT NULL,
+ language_id integer NOT NULL,
+ article_id integer NOT NULL,
+ text text NOT NULL,
+ seo_url character varying,
+ name character varying NOT NULL,
+ preview text,
+ meta_title character varying,
+ meta_descr text,
+ meta_keyword character varying,
+ h1_tag character varying,
+ tag character varying
+);
+
+
+ALTER TABLE article_lang OWNER TO postgres;
+
+--
+-- TOC entry 217 (class 1259 OID 84285)
+-- Name: article_lang_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE article_lang_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE article_lang_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2607 (class 0 OID 0)
+-- Dependencies: 217
+-- Name: article_lang_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE article_lang_id_seq OWNED BY article_lang.article_lang_id;
+
+
+--
+-- TOC entry 208 (class 1259 OID 84084)
+-- Name: article_media; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE article_media (
+ article_media_id integer NOT NULL,
+ article_id integer NOT NULL,
+ media_id integer NOT NULL,
+ type character varying(10) DEFAULT 'additional'::character varying NOT NULL,
+ media_alt character varying,
+ media_title character varying,
+ media_caption character varying,
+ language_id integer
+);
+
+
+ALTER TABLE article_media OWNER TO postgres;
+
+--
+-- TOC entry 218 (class 1259 OID 84288)
+-- Name: article_media_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE article_media_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE article_media_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2608 (class 0 OID 0)
+-- Dependencies: 218
+-- Name: article_media_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE article_media_id_seq OWNED BY article_media.article_media_id;
+
+
+--
+-- TOC entry 212 (class 1259 OID 84120)
+-- Name: article_to_category; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE article_to_category (
+ article_id integer,
+ article_category_id integer
+);
+
+
+ALTER TABLE article_to_category OWNER TO postgres;
+
+--
+-- TOC entry 197 (class 1259 OID 83945)
+-- Name: auth_assignment; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE auth_assignment (
+ item_name character varying(64) NOT NULL,
+ user_id character varying(64) NOT NULL,
+ created_at integer
+);
+
+
+ALTER TABLE auth_assignment OWNER TO postgres;
+
+--
+-- TOC entry 198 (class 1259 OID 83948)
+-- Name: auth_item; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE auth_item (
+ name character varying(64) NOT NULL,
+ type integer NOT NULL,
+ description text,
+ rule_name character varying(64),
+ data text,
+ created_at integer,
+ updated_at integer
+);
+
+
+ALTER TABLE auth_item OWNER TO postgres;
+
+--
+-- TOC entry 199 (class 1259 OID 83954)
+-- Name: auth_item_child; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE auth_item_child (
+ parent character varying(64) NOT NULL,
+ child character varying(64) NOT NULL
+);
+
+
+ALTER TABLE auth_item_child OWNER TO postgres;
+
+--
+-- TOC entry 200 (class 1259 OID 83957)
+-- Name: auth_rule; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE auth_rule (
+ name character varying(64) NOT NULL,
+ data text,
+ created_at integer,
+ updated_at integer
+);
+
+
+ALTER TABLE auth_rule OWNER TO postgres;
+
+--
+-- TOC entry 185 (class 1259 OID 83866)
+-- Name: catalog; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE catalog (
+ catalog_id integer NOT NULL,
+ parent_id integer DEFAULT 0 NOT NULL,
+ cover character varying(32),
+ status integer DEFAULT 1 NOT NULL,
+ sort integer DEFAULT 0 NOT NULL
+);
+
+
+ALTER TABLE catalog OWNER TO postgres;
+
+--
+-- TOC entry 186 (class 1259 OID 83872)
+-- Name: catalog_i18n; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE catalog_i18n (
+ catalog_id integer NOT NULL,
+ lang_id integer DEFAULT 1 NOT NULL,
+ title character varying(1024) NOT NULL,
+ alias character varying(128) NOT NULL,
+ meta_title character varying(255),
+ meta_keywords character varying(255),
+ meta_description character varying(255),
+ full_alias character varying(255)
+);
+
+
+ALTER TABLE catalog_i18n OWNER TO postgres;
+
+--
+-- TOC entry 187 (class 1259 OID 83879)
+-- Name: catalog_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE catalog_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE catalog_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2609 (class 0 OID 0)
+-- Dependencies: 187
+-- Name: catalog_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE catalog_id_seq OWNED BY catalog.catalog_id;
+
+
+--
+-- TOC entry 172 (class 1259 OID 83741)
+-- Name: language; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE language (
+ language_id integer NOT NULL,
+ language_code character varying(4),
+ is_default integer DEFAULT 0,
+ language_name character varying(255),
+ status integer DEFAULT 0,
+ country_code character(4) DEFAULT 0 NOT NULL
+);
+
+
+ALTER TABLE language OWNER TO postgres;
+
+--
+-- TOC entry 173 (class 1259 OID 83746)
+-- Name: language_lang; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE language_lang (
+ language_id integer NOT NULL,
+ lang_title character varying(250) NOT NULL,
+ lang_id integer NOT NULL
+);
+
+
+ALTER TABLE language_lang OWNER TO postgres;
+
+--
+-- TOC entry 174 (class 1259 OID 83749)
+-- Name: language_language_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE language_language_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE language_language_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2610 (class 0 OID 0)
+-- Dependencies: 174
+-- Name: language_language_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE language_language_id_seq OWNED BY language.language_id;
+
+
+--
+-- TOC entry 209 (class 1259 OID 84091)
+-- Name: media; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE media (
+ media_id integer NOT NULL,
+ hash character varying NOT NULL,
+ extension character varying
+);
+
+
+ALTER TABLE media OWNER TO postgres;
+
+--
+-- TOC entry 221 (class 1259 OID 84297)
+-- Name: media_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE media_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE media_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2611 (class 0 OID 0)
+-- Dependencies: 221
+-- Name: media_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE media_id_seq OWNED BY media.media_id;
+
+
+--
+-- TOC entry 175 (class 1259 OID 83751)
+-- Name: menu; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE menu (
+ menu_id integer NOT NULL,
+ menu_pid integer NOT NULL,
+ level integer NOT NULL,
+ termin_id integer NOT NULL,
+ show integer NOT NULL,
+ is_open integer NOT NULL,
+ menu_location_id integer NOT NULL,
+ sortorder integer NOT NULL,
+ name character varying(250) DEFAULT NULL::character varying,
+ url character varying(250) DEFAULT NULL::character varying
+);
+
+
+ALTER TABLE menu OWNER TO postgres;
+
+--
+-- TOC entry 176 (class 1259 OID 83759)
+-- Name: menu_location; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE menu_location (
+ menu_location_id integer NOT NULL,
+ menu_location_name character varying(250)
+);
+
+
+ALTER TABLE menu_location OWNER TO postgres;
+
+--
+-- TOC entry 177 (class 1259 OID 83762)
+-- Name: menu_location_lang; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE menu_location_lang (
+ menu_location_id integer NOT NULL,
+ menu_location_title character varying(250) NOT NULL,
+ lang_id integer NOT NULL
+);
+
+
+ALTER TABLE menu_location_lang OWNER TO postgres;
+
+--
+-- TOC entry 210 (class 1259 OID 84097)
+-- Name: option; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE option (
+ model character varying(200) DEFAULT NULL::character varying,
+ option_id integer NOT NULL,
+ model_id integer,
+ name character varying(200) DEFAULT NULL::character varying,
+ template character varying(200) DEFAULT NULL::character varying,
+ option_pid integer,
+ translate bit(1) DEFAULT NULL::"bit",
+ date_add timestamp without time zone DEFAULT now()
+);
+
+
+ALTER TABLE option OWNER TO postgres;
+
+--
+-- TOC entry 219 (class 1259 OID 84291)
+-- Name: option_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE option_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE option_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2612 (class 0 OID 0)
+-- Dependencies: 219
+-- Name: option_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE option_id_seq OWNED BY option.option_id;
+
+
+--
+-- TOC entry 211 (class 1259 OID 84108)
+-- Name: option_lang; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE option_lang (
+ option_lang_id integer NOT NULL,
+ option_id integer,
+ language_id integer,
+ value text
+);
+
+
+ALTER TABLE option_lang OWNER TO postgres;
+
+--
+-- TOC entry 220 (class 1259 OID 84294)
+-- Name: option_lang_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE option_lang_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE option_lang_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2613 (class 0 OID 0)
+-- Dependencies: 220
+-- Name: option_lang_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE option_lang_id_seq OWNED BY option_lang.option_lang_id;
+
+
+--
+-- TOC entry 201 (class 1259 OID 83963)
+-- Name: social; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE social (
+ social_name character(255),
+ social_user_id integer,
+ user_id integer,
+ social_id integer NOT NULL
+);
+
+
+ALTER TABLE social OWNER TO postgres;
+
+--
+-- TOC entry 202 (class 1259 OID 83966)
+-- Name: social_social_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE social_social_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE social_social_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2614 (class 0 OID 0)
+-- Dependencies: 202
+-- Name: social_social_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE social_social_id_seq OWNED BY social.social_id;
+
+
+--
+-- TOC entry 178 (class 1259 OID 83765)
+-- Name: termin; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE termin (
+ termin_id integer NOT NULL,
+ termin_name character varying(250),
+ is_book integer
+);
+
+
+ALTER TABLE termin OWNER TO postgres;
+
+--
+-- TOC entry 188 (class 1259 OID 83881)
+-- Name: termin_book; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE termin_book (
+ termin_book_id integer NOT NULL,
+ termin_book_name character varying(250),
+ sortorder integer
+);
+
+
+ALTER TABLE termin_book OWNER TO postgres;
+
+--
+-- TOC entry 189 (class 1259 OID 83884)
+-- Name: termin_book_book_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE termin_book_book_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE termin_book_book_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2615 (class 0 OID 0)
+-- Dependencies: 189
+-- Name: termin_book_book_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE termin_book_book_id_seq OWNED BY termin_book.termin_book_id;
+
+
+--
+-- TOC entry 190 (class 1259 OID 83886)
+-- Name: termin_book_lang; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE termin_book_lang (
+ termin_book_id integer NOT NULL,
+ lang_id integer NOT NULL,
+ termin_book_title character varying(250)
+);
+
+
+ALTER TABLE termin_book_lang OWNER TO postgres;
+
+--
+-- TOC entry 179 (class 1259 OID 83768)
+-- Name: termin_lang; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE termin_lang (
+ termin_id integer NOT NULL,
+ language_id integer NOT NULL,
+ termin_title character varying(250),
+ termin_alias character varying(250)
+);
+
+
+ALTER TABLE termin_lang OWNER TO postgres;
+
+--
+-- TOC entry 180 (class 1259 OID 83774)
+-- Name: termin_relation; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE termin_relation (
+ termin_id integer,
+ termin_id_related integer
+);
+
+
+ALTER TABLE termin_relation OWNER TO postgres;
+
+--
+-- TOC entry 181 (class 1259 OID 83777)
+-- Name: termin_structure; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE termin_structure (
+ termin_id integer NOT NULL,
+ termin_pid integer NOT NULL
+);
+
+
+ALTER TABLE termin_structure OWNER TO postgres;
+
+--
+-- TOC entry 182 (class 1259 OID 83780)
+-- Name: termin_termin_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE termin_termin_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE termin_termin_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2616 (class 0 OID 0)
+-- Dependencies: 182
+-- Name: termin_termin_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE termin_termin_id_seq OWNED BY termin.termin_id;
+
+
+--
+-- TOC entry 183 (class 1259 OID 83782)
+-- Name: user; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE TABLE "user" (
+ id integer NOT NULL,
+ username character varying,
+ lastname character varying,
+ firstname character varying,
+ middlename character varying,
+ auth_key character varying(32),
+ password_hash character varying,
+ password_reset_token character varying,
+ email character varying,
+ status smallint,
+ created_at integer,
+ updated_at integer
+);
+
+
+ALTER TABLE "user" OWNER TO postgres;
+
+--
+-- TOC entry 184 (class 1259 OID 83788)
+-- Name: user_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE user_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+ALTER TABLE user_id_seq OWNER TO postgres;
+
+--
+-- TOC entry 2617 (class 0 OID 0)
+-- Dependencies: 184
+-- Name: user_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+--
+
+ALTER SEQUENCE user_id_seq OWNED BY "user".id;
+
+
+--
+-- TOC entry 2247 (class 2604 OID 91932)
+-- Name: admin_menu_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY admin_menu ALTER COLUMN admin_menu_id SET DEFAULT nextval('admin_menu_id_seq'::regclass);
+
+
+--
+-- TOC entry 2248 (class 2604 OID 91933)
+-- Name: admin_menu_access_group_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY admin_menu_access_group ALTER COLUMN admin_menu_access_group_id SET DEFAULT nextval('admin_menu_access_group_id_seq'::regclass);
+
+
+--
+-- TOC entry 2249 (class 2604 OID 91934)
+-- Name: admin_menu_access_user_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY admin_menu_access_user ALTER COLUMN admin_menu_access_user_id SET DEFAULT nextval('admin_menu_access_user_id_seq'::regclass);
+
+
+--
+-- TOC entry 2257 (class 2604 OID 91935)
+-- Name: article_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article ALTER COLUMN article_id SET DEFAULT nextval('article_id_seq'::regclass);
+
+
+--
+-- TOC entry 2262 (class 2604 OID 91936)
+-- Name: article_category_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category ALTER COLUMN article_category_id SET DEFAULT nextval('article_category_id_seq'::regclass);
+
+
+--
+-- TOC entry 2264 (class 2604 OID 91937)
+-- Name: article_category_lang_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category_lang ALTER COLUMN article_category_lang_id SET DEFAULT nextval('article_category_lang_id_seq'::regclass);
+
+
+--
+-- TOC entry 2266 (class 2604 OID 91938)
+-- Name: article_category_media_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category_media ALTER COLUMN article_category_media_id SET DEFAULT nextval('article_category_media_id_seq'::regclass);
+
+
+--
+-- TOC entry 2267 (class 2604 OID 91939)
+-- Name: article_lang_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_lang ALTER COLUMN article_lang_id SET DEFAULT nextval('article_lang_id_seq'::regclass);
+
+
+--
+-- TOC entry 2269 (class 2604 OID 91940)
+-- Name: article_media_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_media ALTER COLUMN article_media_id SET DEFAULT nextval('article_media_id_seq'::regclass);
+
+
+--
+-- TOC entry 2242 (class 2604 OID 83889)
+-- Name: catalog_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY catalog ALTER COLUMN catalog_id SET DEFAULT nextval('catalog_id_seq'::regclass);
+
+
+--
+-- TOC entry 2234 (class 2604 OID 91941)
+-- Name: language_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY language ALTER COLUMN language_id SET DEFAULT nextval('language_language_id_seq'::regclass);
+
+
+--
+-- TOC entry 2270 (class 2604 OID 91942)
+-- Name: media_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY media ALTER COLUMN media_id SET DEFAULT nextval('media_id_seq'::regclass);
+
+
+--
+-- TOC entry 2276 (class 2604 OID 91943)
+-- Name: option_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY option ALTER COLUMN option_id SET DEFAULT nextval('option_id_seq'::regclass);
+
+
+--
+-- TOC entry 2277 (class 2604 OID 91944)
+-- Name: option_lang_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY option_lang ALTER COLUMN option_lang_id SET DEFAULT nextval('option_lang_id_seq'::regclass);
+
+
+--
+-- TOC entry 2250 (class 2604 OID 91945)
+-- Name: social_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY social ALTER COLUMN social_id SET DEFAULT nextval('social_social_id_seq'::regclass);
+
+
+--
+-- TOC entry 2237 (class 2604 OID 91946)
+-- Name: termin_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY termin ALTER COLUMN termin_id SET DEFAULT nextval('termin_termin_id_seq'::regclass);
+
+
+--
+-- TOC entry 2244 (class 2604 OID 91947)
+-- Name: termin_book_id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY termin_book ALTER COLUMN termin_book_id SET DEFAULT nextval('termin_book_book_id_seq'::regclass);
+
+
+--
+-- TOC entry 2238 (class 2604 OID 91948)
+-- Name: id; Type: DEFAULT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY "user" ALTER COLUMN id SET DEFAULT nextval('user_id_seq'::regclass);
+
+
+--
+-- TOC entry 2559 (class 0 OID 83922)
+-- Dependencies: 191
+-- Data for Name: admin_menu; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY admin_menu (admin_menu_id, admin_menu_pid, status, hide_min, sort, name, path, param) FROM stdin;
+15 \N 1 0 1 Текстовые материалы \N \N
+16 15 1 0 1 Статьи /blog/article \N
+17 15 1 0 1 Категории /blog/category \N
+\.
+
+
+--
+-- TOC entry 2560 (class 0 OID 83930)
+-- Dependencies: 192
+-- Data for Name: admin_menu_access_group; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY admin_menu_access_group (admin_menu_access_group_id, admin_menu_id, "group") FROM stdin;
+\.
+
+
+--
+-- TOC entry 2618 (class 0 OID 0)
+-- Dependencies: 193
+-- Name: admin_menu_access_group_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('admin_menu_access_group_id_seq', 1, false);
+
+
+--
+-- TOC entry 2562 (class 0 OID 83938)
+-- Dependencies: 194
+-- Data for Name: admin_menu_access_user; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY admin_menu_access_user (admin_menu_id, user_id, admin_menu_access_user_id) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2619 (class 0 OID 0)
+-- Dependencies: 195
+-- Name: admin_menu_access_user_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('admin_menu_access_user_id_seq', 1, false);
+
+
+--
+-- TOC entry 2620 (class 0 OID 0)
+-- Dependencies: 196
+-- Name: admin_menu_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('admin_menu_id_seq', 20, true);
+
+
+--
+-- TOC entry 2571 (class 0 OID 84041)
+-- Dependencies: 203
+-- Data for Name: article; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY article (article_id, sort, date_add, date_update, code, user_id, tag, article_pid, status, comment, vote) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2572 (class 0 OID 84054)
+-- Dependencies: 204
+-- Data for Name: article_category; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY article_category (article_category_id, sort, code, date_add, date_update, tag, article_category_pid, status) FROM stdin;
+2 100 static 15:22:39.027 15:22:39.027 \N \N 1
+\.
+
+
+--
+-- TOC entry 2621 (class 0 OID 0)
+-- Dependencies: 213
+-- Name: article_category_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('article_category_id_seq', 2, true);
+
+
+--
+-- TOC entry 2573 (class 0 OID 84064)
+-- Dependencies: 205
+-- Data for Name: article_category_lang; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY article_category_lang (article_category_lang_id, language_id, article_category_id, text, preview, seo_url, name, meta_title, meta_descr, meta_keyword, h1_tag, tag) FROM stdin;
+3 1 2 Статические страницы \N \N Статические страницы \N \N \N \N \N
+\.
+
+
+--
+-- TOC entry 2622 (class 0 OID 0)
+-- Dependencies: 215
+-- Name: article_category_lang_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('article_category_lang_id_seq', 3, true);
+
+
+--
+-- TOC entry 2574 (class 0 OID 84071)
+-- Dependencies: 206
+-- Data for Name: article_category_media; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY article_category_media (article_category_media_id, article_category_id, media_id, media_alt, media_title, media_caption, type, language_id) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2623 (class 0 OID 0)
+-- Dependencies: 216
+-- Name: article_category_media_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('article_category_media_id_seq', 1, true);
+
+
+--
+-- TOC entry 2624 (class 0 OID 0)
+-- Dependencies: 214
+-- Name: article_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('article_id_seq', 1, true);
+
+
+--
+-- TOC entry 2575 (class 0 OID 84078)
+-- Dependencies: 207
+-- Data for Name: article_lang; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY article_lang (article_lang_id, language_id, article_id, text, seo_url, name, preview, meta_title, meta_descr, meta_keyword, h1_tag, tag) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2625 (class 0 OID 0)
+-- Dependencies: 217
+-- Name: article_lang_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('article_lang_id_seq', 1, true);
+
+
+--
+-- TOC entry 2576 (class 0 OID 84084)
+-- Dependencies: 208
+-- Data for Name: article_media; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY article_media (article_media_id, article_id, media_id, type, media_alt, media_title, media_caption, language_id) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2626 (class 0 OID 0)
+-- Dependencies: 218
+-- Name: article_media_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('article_media_id_seq', 1, true);
+
+
+--
+-- TOC entry 2580 (class 0 OID 84120)
+-- Dependencies: 212
+-- Data for Name: article_to_category; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY article_to_category (article_id, article_category_id) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2565 (class 0 OID 83945)
+-- Dependencies: 197
+-- Data for Name: auth_assignment; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY auth_assignment (item_name, user_id, created_at) FROM stdin;
+CHUVAK 3 1451029941
+\.
+
+
+--
+-- TOC entry 2566 (class 0 OID 83948)
+-- Dependencies: 198
+-- Data for Name: auth_item; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY auth_item (name, type, description, rule_name, data, created_at, updated_at) FROM stdin;
+site/index 2 Админка: Panel \N \N 1451029912 1451029912
+CHUVAK 1 Чувак \N \N 1451029928 1451030016
+\.
+
+
+--
+-- TOC entry 2567 (class 0 OID 83954)
+-- Dependencies: 199
+-- Data for Name: auth_item_child; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY auth_item_child (parent, child) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2568 (class 0 OID 83957)
+-- Dependencies: 200
+-- Data for Name: auth_rule; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY auth_rule (name, data, created_at, updated_at) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2553 (class 0 OID 83866)
+-- Dependencies: 185
+-- Data for Name: catalog; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY catalog (catalog_id, parent_id, cover, status, sort) FROM stdin;
+1 0 1 10
+314 1 a86cdca816e861b7126e3bd862849907 1 10
+360 313 2f6d6300b3e46a984607a7de31b9f6f5 1 4
+500 1 db18f1ac03dd7df51521346fdffe178e 1 0
+5 2 f39f59a2372134f0a234bec00196e037 1 70
+356 312 2aa426186b7cc1166d713bdd5acd5198 1 1
+420 311 beb951beef428c443e311d612eea70de 1 14
+429 313 3b6b0ee9cd34df727c7908bbf6b164e3 1 5
+464 311 e0bca211e018f9dd33839c19a55dc69a 1 16
+503 2 342d5d65322043c0592afd58bd67f640 1 0
+4 0 1d570fc53882f89cdca63dcc0e36f9fd 1 40
+501 310 c6da3851260f1f8b7b8bd15bf22fcf55 1 0
+414 311 3b8998335be163dd6da9cf03d223161a 1 2
+508 507 1 0
+317 5 e64447c583e7dfdd3e47dbb7c8c6175c 1 0
+361 313 0b05c19c0d88a4d5b18dc6d894e6fa9c 1 7
+423 311 5d863a048576d742b63317a63db8b9ba 1 3
+3 0 1 30
+407 313 b5886a3df55fa7cf394f30f015b9919e 1 3
+318 3 852e6ab206e52f32dfaf5b2f25885b2c 1 10
+426 313 5dd82c57bae0a9383523fba1d9437530 1 16
+351 310 e2fd439ea00e57535118438d716a40a4 1 44
+465 311 7a1cf8992d6703f4fbb84ff3576c0e7d 1 19
+425 313 b97da7768db34a2bcb2f678388222d2d 1 15
+7 -1 \N 1 0
+468 313 5957a30acd7cc7ec24b0d09a7a0cb8a9 1 13
+364 313 3939ded7a340396e0e695d8e0ac366e5 1 21
+416 4 06f96e48837c219aeba9a36c6f7bff74 1 20
+474 471 45c01f63ba3eac9cc854a5a50481441b 1 120
+319 3 c3aafb13358c0d80cde59bc6642f0079 1 20
+369 313 08b4ce04e07f2622daef3c9902cc545a 1 37
+365 313 2475d9eaf2cb76bc84ed1dc97cd9eb70 1 25
+499 312 9f85054926f425eb0fe150289d8300cb 1 0
+430 313 c0e71e3360753c2db33c5f25f2cf0144 1 10
+409 312 aa404dd70a98016dc18ce86ba45dd9c8 1 5
+2 0 1 20
+505 3 528b7a793bd403db5007bc7513ec9c2f 1 0
+405 313 3ec4682e1dc5d526829da5a0082331b5 1 9
+507 0 1 0
+302 1 42bbe35fea913831736036607696ed4f 1 260
+502 1 aa33eaa51b6971b39b9d7a0e5b20f0f1 1 0
+417 311 c9b76cdf3e65b3f5111fb0e0b8d4bda2 1 7
+307 1 fa4083cf4bfd8cd8d6712c8865ecb59c 1 40
+473 471 c4eea90688c384aff846fd0f372e5f02 1 100
+308 1 e5f511a0d3ec84fbe949505b6bacb747 1 180
+476 471 234488c65d17cee0cd51c0fa6689409f 1 130
+309 1 04d31e091f9f19111571e7bc521da958 1 20
+322 1 83cab130683d5bebdf2724e7653071ab 1 50
+325 1 37f61c354cb06de9a95a944b78dd3d13 1 220
+341 1 eaae2bf66d4703a07dc45e8f7612de75 1 120
+340 1 80f8881be6b65813757b85f9f4ce85ef 1 130
+422 311 fa1107964fc9ba2903dfa75bf60c6cb6 1 15
+494 471 7b8dde09e19df6967d33ba8acea0b545 1 40
+484 479 45cd97db0e9918673436015621473da4 1 0
+481 479 173d6d7184a08ad4aab1a43f6b57315b 1 10
+428 313 a4bf15a031ddf961608f83a8b5a59dac 1 12
+444 310 3887e92439187088e6d5f6e0607677bb 1 31
+490 471 35da46429eba293a02a10beddf8521f5 1 10
+486 310 72d95d7ad5d04cdd822b70ff9c3d5d90 1 22
+315 4 1d570fc53882f89cdca63dcc0e36f9fd 1 10
+367 313 af907b8b8a9f320340b7dc168ba7ce0a 1 30
+485 313 ab83801724d061331ca1ab115688f2b1 1 26
+443 313 60567bfe4a321c54778edf0ae5fc6ecf 1 29
+478 310 a1d8526dd6bbdd3a0fe2ef4b677f2adb 1 30
+310 2 53f2f3deedb602a0201e3de26c7f4720 1 10
+471 2 dd44ab9348441a03d05a20d49c72099f 1 60
+479 0 1 60
+432 313 e9d9b418971bf4267bb5ea5242ca98a5 1 11
+384 310 72e8600bbea953e06def60d23d7e681f 1 12
+312 2 cb894bbb88b83ee982938c99b7919c90 1 20
+411 312 6bb52f2ed6d2e8297620148c1dd8ce0b 1 8
+477 310 9b76608b4eac1a1b1fbb6366279e27b2 1 46
+366 313 e23946ee098ee0933883df904d7ca92c 1 27
+441 313 6f633b5953ec9fb90278b8aabef9073b 1 18
+313 2 2fb524a16a98347619e5214463c47837 1 30
+316 5 e382c447a3649b33690f50b57aac3275 1 0
+504 1 494308e419afefa1c90c3fbad1e44c27 1 0
+480 479 8dc615d8b0d97a57c526548379c6a729 1 100
+492 471 5a3b17c28b8d1cec751ca15c01a65f22 1 20
+493 471 dd44ab9348441a03d05a20d49c72099f 1 50
+442 313 c5b93e19e0b29ffe08eeb9688d68184a 1 24
+383 310 8e820420b82ba3df24394bf88ab91147 1 11
+482 479 9ed319224fae76a964a08f03fb312f47 1 0
+466 313 a2a861406cf4f02a38b47b89aec733bc 1 28
+304 1 4855cabc159ad25b6ad3c69eb0f719d2 1 280
+489 479 af496fe78176a59bc130cb7614f6af97 1 0
+410 312 ef2e3c69e2689e858876afce14350d38 1 2
+362 313 20b4a9ecab2f76ae158867bc1524abe5 1 19
+475 471 7df65552edefd5d1455085c9e1f3c1e4 1 110
+311 2 c52ca7a300683dd5c6ba9554003347e6 1 40
+491 471 cdde3f0b854ea6233c4d67fc8b85ff86 1 30
+386 310 85b49aee51415f202a165b258663ce2f 1 14
+385 310 8d763ffe2e71c3039b1bc4e6a1af6310 1 13
+6 0 59045541d4ee79244155d87136c0d765 1 50
+440 313 06ff041f50615dc2f59591040df2468c 1 14
+321 1 b0354f137dbef3450933b57e240f1258 1 80
+363 313 e8a58b648b0fe1e82afc7ca4325aaef3 1 20
+342 311 81dd0f6f9ebf5d4e365c9490b1ad1368 1 17
+381 310 035af82c2a9940466310b31601575d5e 1 9
+483 312 496cd76cf74c5a4b3c1c92435def5585 1 4
+305 1 739ae151b905eca586fd72852af07a8e 1 30
+357 312 0fcc5b5b773eab43ed5fafb7f69bc8bb 1 6
+427 313 10036655778a558b4a427833aa4765b8 1 17
+343 311 a86e60fea62911fdb504cf5292e296fb 1 1
+320 6 59045541d4ee79244155d87136c0d765 1 0
+498 313 eab7b7785ab65d3445eb94de194fb361 1 0
+469 313 d46227f90963c873580b64f0f102764b 1 35
+424 313 5b7d8639fa2fcfe748e609a0d51659d0 1 6
+382 310 e0afa82f2c7c590bec8611792b5aa61c 1 10
+303 1 a4a3800928b2b631d8ebd2027821bc27 1 270
+323 1 03566caf6adcb0279163cca11890e764 1 60
+324 1 b66f455e2a249170527b74ff2ec96654 1 70
+327 1 e4d10b6010efe1cd148b6e8225661c72 1 100
+328 1 adc11390ae3a161ee6d5763b3681aeec 1 110
+390 310 c48ec3e0a5d24da30462670604be2319 1 19
+329 1 a1370d96a6db9c95c43626f354763528 1 170
+330 1 231da0eb79ecbac0d79d6b9957d96bc7 1 140
+339 1 987ade276e3cf9a38d39e4ff3b94c27c 1 150
+389 310 5961b22fc0822a0eff6ac3431033e2ec 1 18
+338 1 203ae14e840f56e6cdd5ab4d4c6905cb 1 160
+337 1 9c1c89a945a4b76a114ba2e0b8afddbe 1 190
+336 1 ea73901ea56199b71d4b14a6a980a48f 1 310
+335 1 40b9943f18ec46f85ecbe5ab62936b93 1 200
+392 310 a16d96b31a614e44f093e186312cb440 1 21
+326 1 195a6cc0838249a5a95a4b416b708b42 1 90
+334 1 299868682db7da738a0ea27104c94ac0 1 210
+402 310 7411cffccdc83f0ae2731ec37fec72f4 1 35
+332 1 42786a5ab258e8d8e33429ebdcd488be 1 240
+306 1 230f1bce5670d611a2d0422585c38b28 1 300
+421 311 b75bd396f787e10fdffcaef73112c993 1 13
+472 310 041ff1a51580cb27d851e4597b667f6f 1 48
+391 310 3c93029d35816f535a5ce3f28b5f0c3e 1 20
+470 313 2133c684296cc56910496380ad256435 1 36
+396 310 a3addf1e476826527ad9ff54befdb137 1 25
+408 310 d16a78a180366dba71e94764c7250090 1 16
+368 313 4d8695f9e79231f8aa018dd0356823eb 1 34
+376 310 c3aa6ff3b91ac85cfd8cc9360172e359 1 3
+359 313 c2ff7d9e432743bd400a65de76a38f12 1 2
+495 313 05fd7985eb0561ea8df3d4d62d85452e 1 0
+344 311 dfefaae6fcdd670499669af84c7f15b1 1 18
+358 313 b9f1f4090377962bd321b9a15fc4a7c0 1 1
+345 311 cd27bc4c4d85edbaa7dc3835bf712b7c 1 9
+373 313 07afb8aad58c23e569d69a4bd1a83c65 1 33
+447 313 7d0b0852cc9e6b30db07f47a87ca1e3d 1 8
+413 312 793ecb42cc9ba60860166c2d8b57ee58 1 3
+446 313 1e16501910fc0a68d89985ae8b0b6144 1 32
+406 310 67b10c6ff02ad4c4fa5b137aeffc3424 1 22
+400 310 4eacac0724dd0f23f395db2617917ed7 1 42
+333 1 accf58010805fed607c5edbe9776262d 1 230
+331 1 9036ed0c936c754afc175640ef956276 1 290
+301 1 49709a7174d3d5e19fe4aa6950fd8b61 1 250
+488 310 e6d3be42662dcb90a22b39dfdc1dc102 1 0
+379 310 643f353b312e6c0958d174c8b5af5916 1 7
+449 310 8f5a81fd78040b1731338d0e486a6342 1 27
+352 310 e485db1a69d72e418f9fa26b29bfc1ea 1 41
+349 310 e242a27198e1d81cf11b466e26b98210 1 47
+403 310 131accbe820b92818ba5e13a31e64e0c 1 43
+394 310 3d326c7435c0d9a931588413db503d74 1 23
+348 311 48536c6e3df7fe4933325fe983950748 1 5
+347 311 b5e0555765e3928ba40840a47fa5f4c1 1 4
+418 311 0dbb654f517f17431785106cf97e236d 1 8
+387 310 a1c7e76a0cb80c9d3eda86a2e5775767 1 15
+454 311 e758ba3a1940316f4d1b153d9c224d0e 1 0
+459 310 3a5c469200ddceb5991daf6d6cc14248 1 39
+448 310 44e67ba3b1aafade714e994ded15b58b 1 4
+346 311 ee398befead5339909c9881a26b4e709 1 12
+506 479 9885290dd6ddf9c79c818a2aa121f09b 1 60
+388 310 62e75eb9aac96b47845b3b5363b31bb2 1 17
+378 310 01d7f411c0f2173079b9a9878c719d49 1 6
+397 310 0183eaba4fe0c472340dbc60a1914a9d 1 28
+350 310 6a603cbcfba99626293efe1352910eee 1 45
+398 310 af098cbaa5609c83af3755ca05a168c5 1 32
+401 310 4162918ffd6e1399c66cd5460f267d1d 1 34
+450 310 50ef8013934e17dbb28b3652f232fd94 1 29
+458 310 c7172400254aeb48ae32271d3555614f 1 37
+395 310 ba18197ebb046a82b566d20d4e5424fc 1 24
+374 310 9b2d6678ab4b0a35100a0f6068d52324 1 1
+412 312 e684e44e63f7ee26107a4eb6e4f0e52d 1 7
+370 313 7d2d5a6e3abca1f173c9de88ddbf0751 1 23
+372 313 364449f8ea9626587992fc39df1d0000 1 22
+371 310 35baa9641207a42038f589aa88323eba 1 36
+380 310 45bfd418d530b803b2f9355ada229f45 1 8
+353 310 93f94af28825189649ecd620cbd9f426 1 38
+399 311 adc2518af74bb3517ae684b1c6b5a19d 1 11
+460 310 e1614e4806bd73ea9e5e9e7c4fb1942b 1 40
+415 311 f1fb4a4dacd99063bf034e5dea573980 1 6
+355 310 75caf3231eafb29183e9d1325f1edee8 1 31
+377 310 efce4ef92ac6e39cc7453885043af125 1 5
+354 310 e7fed88f1941c6d2ecea0f616c05ab64 1 33
+393 310 9ca8b9d8bbcae8f0d4d1ac9b44a0f7a9 1 26
+419 311 cb7ab1b365a28932850222bddef1cfd0 1 10
+375 310 b24f5b2885d1a54f4ef690017e352aa3 1 2
+404 2 8a75bb107bd778510e9211b1be02976b 1 50
+\.
+
+
+--
+-- TOC entry 2554 (class 0 OID 83872)
+-- Dependencies: 186
+-- Data for Name: catalog_i18n; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY catalog_i18n (catalog_id, lang_id, title, alias, meta_title, meta_keywords, meta_description, full_alias) FROM stdin;
+343 2 Абутилон abutilon_1c_22 Абутилон Абутилон Абутилон /semena_tsvetov_1c_20--komnatnye_1c_21/abutilon_1c_22
+337 1 Кукурудза цукрова kukurudza_tsukrova_1c1 Кукурудза цукрова Кукурудза цукрова Кукурудза цукрова /nasinnja_ovochiv_1c0/kukurudza_tsukrova_1c1
+506 1 Преса presa_1c1 Преса Преса Преса /tovari_dlja_sadu_ta_gorodu_1c0/presa_1c1
+499 2 Незабудка nezabudka_1c_22 Незабудка Незабудка Незабудка /semena_tsvetov_1c_20--dvuletniki_1c_21/nezabudka_1c_22
+506 2 Пресса presa_1c_21 Пресса Пресса Пресса /tovary_dlja_sada_i_ogoroda_1c_20/presa_1c_21
+336 1 Цибуля tsibulja_1c1 Цибуля Цибуля Цибуля /nasinnja_ovochiv_1c0/tsibulja_1c1
+354 2 Мимулюс mimulyus_1c_22 Мимулюс Мимулюс Мимулюс /semena_tsvetov_1c_20--odnoletniki_1c_21/mimulyus_1c_22
+333 1 Перець perets_1c1 Перець Перець Перець /nasinnja_ovochiv_1c0/perets_1c1
+331 1 Селера selera_1c1 Селера Селера Селера /nasinnja_ovochiv_1c0/selera_1c1
+396 1 Космея kosmos_1c2 Космея Космея Космея /nasinnja_kvitiv_1c0--odnorichniki_1c1/kosmos_1c2
+392 1 Іпомея ipomeja_1c2 Іпомея Іпомея Іпомея /nasinnja_kvitiv_1c0--odnorichniki_1c1/ipomeja_1c2
+494 1 Кала (весняна колекція) kala_vesnjana_kolektsija_1c2 Кала (весняна колекція) Кала (весняна колекція) Кала (весняна колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/kala_vesnjana_kolektsija_1c2
+386 1 Гомфрена gomfrena_1c2 Гомфрена Гомфрена Гомфрена /nasinnja_kvitiv_1c0--odnorichniki_1c1/gomfrena_1c2
+499 1 Незабудка nezabudka_1c2 Незабудка Незабудка Незабудка /nasinnja_kvitiv_1c0--dvorichniki_1c1/nezabudka_1c2
+367 1 Ромашка romashka_1c2 Ромашка Ромашка Ромашка /nasinnja_kvitiv_1c0--bagatorichniki_1c1/romashka_1c2
+391 1 Жоржина zhorzhina_1c2 Жоржина Жоржина Жоржина /nasinnja_kvitiv_1c0--odnorichniki_1c1/zhorzhina_1c2
+479 1 Товари для саду та городу tovari_dlja_sadu_ta_gorodu_1c0 Товари для саду та городу Товари для саду та городу Товари для саду та городу /tovari_dlja_sadu_ta_gorodu_1c0
+425 1 Едельвейс edelveys_1c2 Едельвейс Едельвейс Едельвейс /nasinnja_kvitiv_1c0--bagatorichniki_1c1/edelveys_1c2
+398 2 Матиола matiola_1c_22 Матиола Матиола Матиола /semena_tsvetov_1c_20--odnoletniki_1c_21/matiola_1c_22
+480 1 Грунти та субстрати inshi_tovari_1c1 Грунти та субстрати Грунти та субстрати Грунти та субстрати /tovari_dlja_sadu_ta_gorodu_1c0/inshi_tovari_1c1
+483 1 Лакфіоль lakfiol_1c2 Лакфіоль Лакфіоль Лакфіоль /nasinnja_kvitiv_1c0--dvorichniki_1c1/lakfiol_1c2
+366 2 Платикодон platikodon_1c_22 Платикодон Платикодон Платикодон /semena_tsvetov_1c_20--mnogoletniki_1c_21/platikodon_1c_22
+393 1 Кохія kokhija_1c2 Кохія Кохія Кохія /nasinnja_kvitiv_1c0--odnorichniki_1c1/kokhija_1c2
+426 2 Канна kanna_1c_22 Канна Канна Канна /semena_tsvetov_1c_20--mnogoletniki_1c_21/kanna_1c_22
+368 1 Сон-трава sontrava_1c2 Сон-трава Сон-трава Сон-трава /nasinnja_kvitiv_1c0--bagatorichniki_1c1/sontrava_1c2
+385 1 Годеція godetsija_1c2 Годеція Годеція Годеція /nasinnja_kvitiv_1c0--odnorichniki_1c1/godetsija_1c2
+310 1 Однорічники odnorichniki_1c1 Однорічники Однорічники Однорічники /nasinnja_kvitiv_1c0--odnorichniki_1c1
+394 2 Квамоклит (Мина Лобата) kvamoklit_1c_22 Квамоклит (Мина Лобата) Квамоклит (Мина Лобата) Квамоклит (Мина Лобата) /semena_tsvetov_1c_20--odnoletniki_1c_21/kvamoklit_1c_22
+7 1 Обладнання obladnannia Обладнання Обладнання Обладнання /obladnannia
+384 1 Геліотроп geliotrop_1c2 Геліотроп Геліотроп Геліотроп /nasinnja_kvitiv_1c0--odnorichniki_1c1/geliotrop_1c2
+7 2 Оборудование oborudovanie Оборудование Оборудование Оборудование /oborudovanie
+397 1 Лаватера lavatera_1c2 Лаватера Лаватера Лаватера /nasinnja_kvitiv_1c0--odnorichniki_1c1/lavatera_1c2
+389 1 Еустома eustoma_1c2 Еустома Еустома Еустома /nasinnja_kvitiv_1c0--odnorichniki_1c1/eustoma_1c2
+428 1 Дельфініум delfinium_1c2 Дельфініум Дельфініум Дельфініум /nasinnja_kvitiv_1c0--bagatorichniki_1c1/delfinium_1c2
+444 1 Рудбекія rudbekija_1c2 Рудбекія Рудбекія Рудбекія /nasinnja_kvitiv_1c0--odnorichniki_1c1/rudbekija_1c2
+507 1 обладнання obladnennja_1c0 обладнання обладнання обладнання /obladnennja_1c0
+418 1 Кавове дерево kavove_derevo_1c2 Кавове дерево Кавове дерево Кавове дерево /nasinnja_kvitiv_1c0--kimnatni_1c1/kavove_derevo_1c2
+508 1 обладнання obladnannjax_1c1 обладнання обладнання обладнання /obladnennja_1c0/obladnannjax_1c1
+402 1 Молюцелла molyutsella_1c2 Молюцелла Молюцелла Молюцелла /nasinnja_kvitiv_1c0--odnorichniki_1c1/molyutsella_1c2
+372 1 Люпин lyupin_1c2 Люпин Люпин Люпин /nasinnja_kvitiv_1c0--bagatorichniki_1c1/lyupin_1c2
+495 1 Пенстемон viola_vitrokka_karma_f1x_1c2 Пенстемон Пенстемон Пенстемон /nasinnja_kvitiv_1c0--bagatorichniki_1c1/viola_vitrokka_karma_f1x_1c2
+412 1 Наперстянка naperstjanka_1c2 Наперстянка Наперстянка Наперстянка /nasinnja_kvitiv_1c0--dvorichniki_1c1/naperstjanka_1c2
+484 2 Горшки и кашпо gorshki_i_kashpo_1c_21 Горшки и кашпо Горшки и кашпо Горшки и кашпо /tovary_dlja_sada_i_ogoroda_1c_20/gorshki_i_kashpo_1c_21
+409 1 Мальва malva_1c2 Мальва Мальва Мальва /nasinnja_kvitiv_1c0--dvorichniki_1c1/malva_1c2
+401 1 Мірабіліс mirabilis_1c2 Мірабіліс Мірабіліс Мірабіліс /nasinnja_kvitiv_1c0--odnorichniki_1c1/mirabilis_1c2
+480 2 Грунты и субстраты drugie_tovary_1c_21 Грунты и субстраты Грунты и субстраты Грунты и субстраты /tovary_dlja_sada_i_ogoroda_1c_20/drugie_tovary_1c_21
+481 1 Агро-текстиль agrotekstil_1c1 Агро-текстиль Агро-текстиль Агро-текстиль /tovari_dlja_sadu_ta_gorodu_1c0/agrotekstil_1c1
+502 2 Лук озимый luk_ozimyy_1c_21 Лук озимый Лук озимый Лук озимый /semena_ovoshchey_1c_20/luk_ozimyy_1c_21
+501 1 Схізантус skhizantus_1c2 Схізантус Схізантус Схізантус /nasinnja_kvitiv_1c0--odnorichniki_1c1/skhizantus_1c2
+306 1 Томат tomat_1c1 Томат Томат Томат /nasinnja_ovochiv_1c0/tomat_1c1
+501 2 Схизантус skhizantus_1c_22 Схизантус Схизантус Схизантус /semena_tsvetov_1c_20--odnoletniki_1c_21/skhizantus_1c_22
+373 1 Мак східний sinegolovnik_mikolaychiki_1c2 Мак східний Мак східний Мак східний /nasinnja_kvitiv_1c0--bagatorichniki_1c1/sinegolovnik_mikolaychiki_1c2
+413 1 Дзвоники dzvoniki_1c2 Дзвоники Дзвоники Дзвоники /nasinnja_kvitiv_1c0--dvorichniki_1c1/dzvoniki_1c2
+357 1 Маргаритка (Стокротка) margaritka_stokrotka_1c2 Маргаритка (Стокротка) Маргаритка (Стокротка) Маргаритка (Стокротка) /nasinnja_kvitiv_1c0--dvorichniki_1c1/margaritka_stokrotka_1c2
+376 1 Бакопа bakopa_1c2 Бакопа Бакопа Бакопа /nasinnja_kvitiv_1c0--odnorichniki_1c1/bakopa_1c2
+302 1 Редька redka_1c1 Редька Редька Редька /nasinnja_ovochiv_1c0/redka_1c1
+366 1 Платикодон platikodon_1c2 Платикодон Платикодон Платикодон /nasinnja_kvitiv_1c0--bagatorichniki_1c1/platikodon_1c2
+313 1 Багаторічники bagatorichniki_1c1 Багаторічники Багаторічники Багаторічники /nasinnja_kvitiv_1c0--bagatorichniki_1c1
+371 1 Петунія petunija_1c2 Петунія Петунія Петунія /nasinnja_kvitiv_1c0--odnorichniki_1c1/petunija_1c2
+365 1 Обрієта obriyeta_1c2 Обрієта Обрієта Обрієта /nasinnja_kvitiv_1c0--bagatorichniki_1c1/obriyeta_1c2
+421 1 Мімоза mimoza_1c2 Мімоза Мімоза Мімоза /nasinnja_kvitiv_1c0--kimnatni_1c1/mimoza_1c2
+406 1 Катарантус katarantus_1c2 Катарантус Катарантус Катарантус /nasinnja_kvitiv_1c0--odnorichniki_1c1/katarantus_1c2
+304 1 Салатний мікс salatniy_miks_1c1 Салатний мікс Салатний мікс Салатний мікс /nasinnja_ovochiv_1c0/salatniy_miks_1c1
+408 1 Гіпоестес datura_1c2 Гіпоестес Гіпоестес Гіпоестес /nasinnja_kvitiv_1c0--odnorichniki_1c1/datura_1c2
+339 1 Капуста савойська kapusta_savoyska_1c1 Капуста савойська Капуста савойська Капуста савойська /nasinnja_ovochiv_1c0/kapusta_savoyska_1c1
+505 2 Профессиональные удобрения profesiyni_dobriva_1c_21 Профессиональные удобрения Профессиональные удобрения Профессиональные удобрения /udobrenija_i_sredstva_zashchity_1c_20/profesiyni_dobriva_1c_21
+423 2 Бальзамин balzamin_1c_22 Бальзамин Бальзамин Бальзамин /semena_tsvetov_1c_20--komnatnye_1c_21/balzamin_1c_22
+502 1 Цибуля озима tsibulja_ozima_1c1 Цибуля озима Цибуля озима Цибуля озима /nasinnja_ovochiv_1c0/tsibulja_ozima_1c1
+338 1 Капуста цвітна kapusta_tsvitna_1c1 Капуста цвітна Капуста цвітна Капуста цвітна /nasinnja_ovochiv_1c0/kapusta_tsvitna_1c1
+342 1 Перікалліс (Цинерарія гібридна) perikallis_tsinerarija_gibridna_1c2 Перікалліс (Цинерарія гібридна) Перікалліс (Цинерарія гібридна) Перікалліс (Цинерарія гібридна) /nasinnja_kvitiv_1c0--kimnatni_1c1/perikallis_tsinerarija_gibridna_1c2
+449 1 Красоля krasolja_1c2 Красоля Красоля Красоля /nasinnja_kvitiv_1c0--odnorichniki_1c1/krasolja_1c2
+351 1 Тютюн tyutyun_1c2 Тютюн Тютюн Тютюн /nasinnja_kvitiv_1c0--odnorichniki_1c1/tyutyun_1c2
+394 1 Квамокліт (Міна Лобата) kvamoklit_mina_lobata_1c2 Квамокліт (Міна Лобата) Квамокліт (Міна Лобата) Квамокліт (Міна Лобата) /nasinnja_kvitiv_1c0--odnorichniki_1c1/kvamoklit_mina_lobata_1c2
+447 1 Асклепіас asklepias_1c2 Асклепіас Асклепіас Асклепіас /nasinnja_kvitiv_1c0--bagatorichniki_1c1/asklepias_1c2
+358 1 Айстра альпійська aystra_alpiyska_1c2 Айстра альпійська Айстра альпійська Айстра альпійська /nasinnja_kvitiv_1c0--bagatorichniki_1c1/aystra_alpiyska_1c2
+419 1 Кальцеолярія kaltseoljarija_1c2 Кальцеолярія Кальцеолярія Кальцеолярія /nasinnja_kvitiv_1c0--kimnatni_1c1/kaltseoljarija_1c2
+470 1 Фізостегія fizostegija_1c2 Фізостегія Фізостегія Фізостегія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/fizostegija_1c2
+423 1 Бальзамін balzamin_1c2 Бальзамін Бальзамін Бальзамін /nasinnja_kvitiv_1c0--kimnatni_1c1/balzamin_1c2
+495 2 Пенстемон penstemon_1c_22 Пенстемон Пенстемон Пенстемон /semena_tsvetov_1c_20--mnogoletniki_1c_21/penstemon_1c_22
+321 1 Кавун kavun_1c1 Кавун Кавун Кавун /nasinnja_ovochiv_1c0/kavun_1c1
+488 1 Нагідки nagidki_1c2 Нагідки Нагідки Нагідки /nasinnja_kvitiv_1c0--odnorichniki_1c1/nagidki_1c2
+505 1 Професійні добрива profesiyni_dobriva_1c1 Професійні добрива Професійні добрива Професійні добрива /dobriva_ta_zasobi_zakhistu_1c0/profesiyni_dobriva_1c1
+390 1 Ешольція esholtsija_1c2 Ешольція Ешольція Ешольція /nasinnja_kvitiv_1c0--odnorichniki_1c1/esholtsija_1c2
+323 1 Диня dinja_1c1 Диня Диня Диня /nasinnja_ovochiv_1c0/dinja_1c1
+430 1 Гейхера geykhera_1c2 Гейхера Гейхера Гейхера /nasinnja_kvitiv_1c0--bagatorichniki_1c1/geykhera_1c2
+489 2 Садовый инвентарь sadovyy_inventar_1c_21 Садовый инвентарь Садовый инвентарь Садовый инвентарь /tovary_dlja_sada_i_ogoroda_1c_20/sadovyy_inventar_1c_21
+411 1 Ранункулюс ranunkulyus_1c2 Ранункулюс Ранункулюс Ранункулюс /nasinnja_kvitiv_1c0--dvorichniki_1c1/ranunkulyus_1c2
+464 1 Пентас pentas_1c2 Пентас Пентас Пентас /nasinnja_kvitiv_1c0--kimnatni_1c1/pentas_1c2
+324 1 Кабачок kabachok_1c1 Кабачок Кабачок Кабачок /nasinnja_ovochiv_1c0/kabachok_1c1
+454 2 Циперус fasolx_1c_22 Циперус Циперус Циперус /semena_tsvetov_1c_20--komnatnye_1c_21/fasolx_1c_22
+341 1 Капуста китайська kapusta_kitayska_1c1 Капуста китайська Капуста китайська Капуста китайська /nasinnja_ovochiv_1c0/kapusta_kitayska_1c1
+361 1 Армерія armerija_1c2 Армерія Армерія Армерія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/armerija_1c2
+443 1 Прунелла prunella_1c2 Прунелла Прунелла Прунелла /nasinnja_kvitiv_1c0--bagatorichniki_1c1/prunella_1c2
+375 1 Айстра aystra_1c2 Айстра Айстра Айстра /nasinnja_kvitiv_1c0--odnorichniki_1c1/aystra_1c2
+417 1 Жакаранда zhakaranda_1c2 Жакаранда Жакаранда Жакаранда /nasinnja_kvitiv_1c0--kimnatni_1c1/zhakaranda_1c2
+432 1 Гібіскус gibiskus_1c2 Гібіскус Гібіскус Гібіскус /nasinnja_kvitiv_1c0--bagatorichniki_1c1/gibiskus_1c2
+472 2 Бархатцы strelitsijax_1c_22 Бархатцы Бархатцы Бархатцы /semena_tsvetov_1c_20--odnoletniki_1c_21/strelitsijax_1c_22
+6 1 Біопрепарати biopreparatix_1c0 Біопрепарати Біопрепарати Біопрепарати /biopreparatix_1c0
+440 1 Доронікум doronikum_1c2 Доронікум Доронікум Доронікум /nasinnja_kvitiv_1c0--bagatorichniki_1c1/doronikum_1c2
+326 1 Капуста білоголова kapusta_bilogolova_1c1 Капуста білоголова Капуста білоголова Капуста білоголова /nasinnja_ovochiv_1c0/kapusta_bilogolova_1c1
+363 1 Ліхніс likhnis_1c2 Ліхніс Ліхніс Ліхніс /nasinnja_kvitiv_1c0--bagatorichniki_1c1/likhnis_1c2
+5 1 Квіткові суміші kvitkovi_sumishi_1c1 Квіткові суміші Квіткові суміші Квіткові суміші /nasinnja_kvitiv_1c0--kvitkovi_sumishi_1c1
+330 1 Капуста пекінська kapusta_pekinska_1c1 Капуста пекінська Капуста пекінська Капуста пекінська /nasinnja_ovochiv_1c0/kapusta_pekinska_1c1
+320 2 Биопрепарати biopreparati_1c_21 Биопрепарати Биопрепарати Биопрепарати /biopreparaty_1c_20/biopreparati_1c_21
+441 1 Кореопсіс koreopsis_1c2 Кореопсіс Кореопсіс Кореопсіс /nasinnja_kvitiv_1c0--bagatorichniki_1c1/koreopsis_1c2
+398 1 Матіола matiola_1c2 Матіола Матіола Матіола /nasinnja_kvitiv_1c0--odnorichniki_1c1/matiola_1c2
+340 1 Капуста кольрабі kapusta_kolrabi_1c1 Капуста кольрабі Капуста кольрабі Капуста кольрабі /nasinnja_ovochiv_1c0/kapusta_kolrabi_1c1
+2 1 Насіння квітів nasinnja_kvitiv_1c0 Насіння квітів Насіння квітів Насіння квітів /nasinnja_kvitiv_1c0
+448 1 Бальзамін balzaminx_1c2 Бальзамін Бальзамін Бальзамін /nasinnja_kvitiv_1c0--odnorichniki_1c1/balzaminx_1c2
+422 2 Пеларгония pelargonija_1c_22 Пеларгония Пеларгония Пеларгония /semena_tsvetov_1c_20--komnatnye_1c_21/pelargonija_1c_22
+426 1 Канна kanna_1c2 Канна Канна Канна /nasinnja_kvitiv_1c0--bagatorichniki_1c1/kanna_1c2
+374 1 Агератум ageratum_1c2 Агератум Агератум Агератум /nasinnja_kvitiv_1c0--odnorichniki_1c1/ageratum_1c2
+425 2 Эдельвейс edelveys_1c_22 Эдельвейс Эдельвейс Эдельвейс /semena_tsvetov_1c_20--mnogoletniki_1c_21/edelveys_1c_22
+468 2 Колокольчики kolokolchiki_1c_22 Колокольчики Колокольчики Колокольчики /semena_tsvetov_1c_20--mnogoletniki_1c_21/kolokolchiki_1c_22
+416 1 Насіння сидеральних культур nasinnja_sideralnikh_kultur_1c1 Насіння сидеральних культур Насіння сидеральних культур Насіння сидеральних культур /gazonni_travi_1c0/nasinnja_sideralnikh_kultur_1c1
+442 1 Морозник moroznik_1c2 Морозник Морозник Морозник /nasinnja_kvitiv_1c0--bagatorichniki_1c1/moroznik_1c2
+446 1 Сальвія salvija_1c2 Сальвія Сальвія Сальвія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/salvija_1c2
+4 1 Газонні трави gazonni_travi_1c0 Газонні трави Газонні трави Газонні трави /gazonni_travi_1c0
+427 1 Кніфофія knifofija_1c2 Кніфофія Кніфофія Кніфофія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/knifofija_1c2
+322 1 Горох овочевий gorokh_ovocheviy_1c1 Горох овочевий Горох овочевий Горох овочевий /nasinnja_ovochiv_1c0/gorokh_ovocheviy_1c1
+378 1 Біденс bidens_1c2 Біденс Біденс Біденс /nasinnja_kvitiv_1c0--odnorichniki_1c1/bidens_1c2
+332 1 Пряні та зелені культури prjani_ta_zeleni_kulturi_1c1 Пряні та зелені культури Пряні та зелені культури Пряні та зелені культури /nasinnja_ovochiv_1c0/prjani_ta_zeleni_kulturi_1c1
+303 1 Салат salat_1c1 Салат Салат Салат /nasinnja_ovochiv_1c0/salat_1c1
+380 1 Вербена verbena_1c2 Вербена Вербена Вербена /nasinnja_kvitiv_1c0--odnorichniki_1c1/verbena_1c2
+490 1 Бегонія (весняна колекція) begonija_vesnjana_kolektsija_1c2 Бегонія (весняна колекція) Бегонія (весняна колекція) Бегонія (весняна колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/begonija_vesnjana_kolektsija_1c2
+458 1 Портулак portulak_1c2 Портулак Портулак Портулак /nasinnja_kvitiv_1c0--odnorichniki_1c1/portulak_1c2
+318 1 Добрива dobriva_1c1 Добрива Добрива Добрива /dobriva_ta_zasobi_zakhistu_1c0/dobriva_1c1
+342 2 Перикаллис (Цинерария гибридная) perikallis_tsinerarija_gibridnaja_1c_22 Перикаллис (Цинерария гибридная) Перикаллис (Цинерария гибридная) Перикаллис (Цинерария гибридная) /semena_tsvetov_1c_20--komnatnye_1c_21/perikallis_tsinerarija_gibridnaja_1c_22
+317 1 Novaflore novaflore_1c2 Novaflore Novaflore Novaflore /nasinnja_kvitiv_1c0--kvitkovi_sumishi_1c1/novaflore_1c2
+492 1 Гладіолус (весняна колекція) gladiolus_vesnjana_kolektsija_1c2 Гладіолус (весняна колекція) Гладіолус (весняна колекція) Гладіолус (весняна колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/gladiolus_vesnjana_kolektsija_1c2
+381 1 Волошка voloshka_1c2 Волошка Волошка Волошка /nasinnja_kvitiv_1c0--odnorichniki_1c1/voloshka_1c2
+491 1 Жоржина (весняна колекція) zhorzhina_vesnjana_kolektsija_1c2 Жоржина (весняна колекція) Жоржина (весняна колекція) Жоржина (весняна колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/zhorzhina_vesnjana_kolektsija_1c2
+354 1 Мімулюс mimulyus_1c2 Мімулюс Мімулюс Мімулюс /nasinnja_kvitiv_1c0--odnorichniki_1c1/mimulyus_1c2
+484 1 Горщики і кашпо gorshchiki_i_kashpo_1c1 Горщики і кашпо Горщики і кашпо Горщики і кашпо /tovari_dlja_sadu_ta_gorodu_1c0/gorshchiki_i_kashpo_1c1
+450 1 Лобелія lobelija_1c2 Лобелія Лобелія Лобелія /nasinnja_kvitiv_1c0--odnorichniki_1c1/lobelija_1c2
+357 2 Маргаритка margaritka_deyzi_1c_22 Маргаритка Маргаритка Маргаритка /semena_tsvetov_1c_20--dvuletniki_1c_21/margaritka_deyzi_1c_22
+320 1 Біопрепарати biopreparati_1c1 Біопрепарати Біопрепарати Біопрепарати /biopreparatix_1c0/biopreparati_1c1
+379 1 Брахікома brakhikoma_1c2 Брахікома Брахікома Брахікома /nasinnja_kvitiv_1c0--odnorichniki_1c1/brakhikoma_1c2
+355 1 Мак mak_1c2 Мак Мак Мак /nasinnja_kvitiv_1c0--odnorichniki_1c1/mak_1c2
+377 1 Безсмертник (Геліхризум) bezsmertnik_gelikhrizum_1c2 Безсмертник (Геліхризум) Безсмертник (Геліхризум) Безсмертник (Геліхризум) /nasinnja_kvitiv_1c0--odnorichniki_1c1/bezsmertnik_gelikhrizum_1c2
+382 1 Газанія gazanija_1c2 Газанія Газанія Газанія /nasinnja_kvitiv_1c0--odnorichniki_1c1/gazanija_1c2
+485 1 Перовскія perovskija_1c2 Перовскія Перовскія Перовскія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/perovskija_1c2
+486 1 Калібрахоа kalibrakhoa_1c2 Калібрахоа Калібрахоа Калібрахоа /nasinnja_kvitiv_1c0--odnorichniki_1c1/kalibrakhoa_1c2
+387 1 Горошок goroshok_1c2 Горошок Горошок Горошок /nasinnja_kvitiv_1c0--odnorichniki_1c1/goroshok_1c2
+459 1 Сальвія salvijax_1c2 Сальвія Сальвія Сальвія /nasinnja_kvitiv_1c0--odnorichniki_1c1/salvijax_1c2
+407 1 Аліссум alissum_1c2 Аліссум Аліссум Аліссум /nasinnja_kvitiv_1c0--bagatorichniki_1c1/alissum_1c2
+360 1 Альстремерія alstremerija_1c2 Альстремерія Альстремерія Альстремерія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/alstremerija_1c2
+429 1 Арабіс arabis_1c2 Арабіс Арабіс Арабіс /nasinnja_kvitiv_1c0--bagatorichniki_1c1/arabis_1c2
+376 2 Бакопа bakopa_1c_22 Бакопа Бакопа Бакопа /semena_tsvetov_1c_20--odnoletniki_1c_21/bakopa_1c_22
+396 2 Космея kosmos_1c_22 Космея Космея Космея /semena_tsvetov_1c_20--odnoletniki_1c_21/kosmos_1c_22
+347 1 Банан banan_1c2 Банан Банан Банан /nasinnja_kvitiv_1c0--kimnatni_1c1/banan_1c2
+454 1 Циперус tsiperus_1c2 Циперус Циперус Циперус /nasinnja_kvitiv_1c0--kimnatni_1c1/tsiperus_1c2
+311 1 Кімнатні kimnatni_1c1 Кімнатні Кімнатні Кімнатні /nasinnja_kvitiv_1c0--kimnatni_1c1
+373 2 Синеголовник sinegolovnik_nikolaychiki_1c_22 Синеголовник Синеголовник Синеголовник /semena_tsvetov_1c_20--mnogoletniki_1c_21/sinegolovnik_nikolaychiki_1c_22
+397 2 Лаватера lavatera_1c_22 Лаватера Лаватера Лаватера /semena_tsvetov_1c_20--odnoletniki_1c_21/lavatera_1c_22
+410 1 Гвоздика gvozdika_1c2 Гвоздика Гвоздика Гвоздика /nasinnja_kvitiv_1c0--dvorichniki_1c1/gvozdika_1c2
+386 2 Гомфрена gomfrena_1c_22 Гомфрена Гомфрена Гомфрена /semena_tsvetov_1c_20--odnoletniki_1c_21/gomfrena_1c_22
+385 2 Годеция godetsija_1c_22 Годеция Годеция Годеция /semena_tsvetov_1c_20--odnoletniki_1c_21/godetsija_1c_22
+353 1 Ротики садові (Антіррінум) rotiki_sadovi_antirrinum_1c2 Ротики садові (Антіррінум) Ротики садові (Антіррінум) Ротики садові (Антіррінум) /nasinnja_kvitiv_1c0--odnorichniki_1c1/rotiki_sadovi_antirrinum_1c2
+408 2 Гипоэстес datura_1c_22 Гипоэстес Гипоэстес Гипоэстес /semena_tsvetov_1c_20--odnoletniki_1c_21/datura_1c_22
+356 1 Віола viola_1c2 Віола Віола Віола /nasinnja_kvitiv_1c0--dvorichniki_1c1/viola_1c2
+374 2 Агератум ageratum_1c_22 Агератум Агератум Агератум /semena_tsvetov_1c_20--odnoletniki_1c_21/ageratum_1c_22
+416 2 Семена сидеральных культур semena_sideralnykh_kultur_1c_21 Семена сидеральных культур Семена сидеральных культур Семена сидеральных культур /gazonnye_travy_1c_20/semena_sideralnykh_kultur_1c_21
+475 1 Крокуси (осіння колекція) krokusi_1c2 Крокуси (осіння колекція) Крокуси (осіння колекція) Крокуси (осіння колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/krokusi_1c2
+383 2 Гвоздика китайская gvozdika_kitayskaja_1c_22 Гвоздика китайская Гвоздика китайская Гвоздика китайская /semena_tsvetov_1c_20--odnoletniki_1c_21/gvozdika_kitayskaja_1c_22
+404 1 gfgddfgjdgfj jagidni_1c1 Ягідні Ягідні Ягідні /nasinnja_kvitiv_1c0/jagidni_1c1
+388 1 Діхондра dikhondra_1c2 Діхондра Діхондра Діхондра /nasinnja_kvitiv_1c0--odnorichniki_1c1/dikhondra_1c2
+403 1 Тунбергія tunbergija_1c2 Тунбергія Тунбергія Тунбергія /nasinnja_kvitiv_1c0--odnorichniki_1c1/tunbergija_1c2
+316 1 Elite серія elite_serija_1c2 Elite серія Elite серія Elite серія /nasinnja_kvitiv_1c0--kvitkovi_sumishi_1c1/elite_serija_1c2
+400 1 Торенія torenija_1c2 Торенія Торенія Торенія /nasinnja_kvitiv_1c0--odnorichniki_1c1/torenija_1c2
+384 2 Гелиотроп geliotrop_1c_22 Гелиотроп Гелиотроп Гелиотроп /semena_tsvetov_1c_20--odnoletniki_1c_21/geliotrop_1c_22
+352 1 Статиця statitsja_1c2 Статиця Статиця Статиця /nasinnja_kvitiv_1c0--odnorichniki_1c1/statitsja_1c2
+403 2 Тунбергия tunbergija_1c_22 Тунбергия Тунбергия Тунбергия /semena_tsvetov_1c_20--odnoletniki_1c_21/tunbergija_1c_22
+466 2 Примула udobrenija_1c_22 Примула Примула Примула /semena_tsvetov_1c_20--mnogoletniki_1c_21/udobrenija_1c_22
+405 1 Гвоздика gvozdikax_1c2 Гвоздика Гвоздика Гвоздика /nasinnja_kvitiv_1c0--bagatorichniki_1c1/gvozdikax_1c2
+400 2 Торения torenija_1c_22 Торения Торения Торения /semena_tsvetov_1c_20--odnoletniki_1c_21/torenija_1c_22
+419 2 Кальцеолярия kaltseoljarija_1c_22 Кальцеолярия Кальцеолярия Кальцеолярия /semena_tsvetov_1c_20--komnatnye_1c_21/kaltseoljarija_1c_22
+343 1 Абутілон abutilon_1c2 Абутілон Абутілон Абутілон /nasinnja_kvitiv_1c0--kimnatni_1c1/abutilon_1c2
+345 1 Кактус kaktus_1c2 Кактус Кактус Кактус /nasinnja_kvitiv_1c0--kimnatni_1c1/kaktus_1c2
+309 1 Боби bobi_1c1 Боби Боби Боби /nasinnja_ovochiv_1c0/bobi_1c1
+399 1 Колеус koleus_1c2 Колеус Колеус Колеус /nasinnja_kvitiv_1c0--kimnatni_1c1/koleus_1c2
+464 2 Пентас pentas_1c_22 Пентас Пентас Пентас /semena_tsvetov_1c_20--komnatnye_1c_21/pentas_1c_22
+399 2 Колеус koleus_1c_22 Колеус Колеус Колеус /semena_tsvetov_1c_20--komnatnye_1c_21/koleus_1c_22
+411 2 Ранункулюс ranunkulyus_1c_22 Ранункулюс Ранункулюс Ранункулюс /semena_tsvetov_1c_20--dvuletniki_1c_21/ranunkulyus_1c_22
+430 2 Гейхера sredstva_zashchity_1c_22 Гейхера Гейхера Гейхера /semena_tsvetov_1c_20--mnogoletniki_1c_21/sredstva_zashchity_1c_22
+422 1 Пеларгонія pelargonija_1c2 Пеларгонія Пеларгонія Пеларгонія /nasinnja_kvitiv_1c0--kimnatni_1c1/pelargonija_1c2
+402 2 Молюцелла molyutsella_1c_22 Молюцелла Молюцелла Молюцелла /semena_tsvetov_1c_20--odnoletniki_1c_21/molyutsella_1c_22
+362 1 Гіпсофіла leshchitsja_gipsofila_1c2 Гіпсофіла Гіпсофіла Гіпсофіла /nasinnja_kvitiv_1c0--bagatorichniki_1c1/leshchitsja_gipsofila_1c2
+390 2 Эшшольция eshsholtsija_1c_22 Эшшольция Эшшольция Эшшольция /semena_tsvetov_1c_20--odnoletniki_1c_21/eshsholtsija_1c_22
+351 2 Табак tabak_1c_22 Табак Табак Табак /semena_tsvetov_1c_20--odnoletniki_1c_21/tabak_1c_22
+449 2 Настурция nasturtsija_1c_22 Настурция Настурция Настурция /semena_tsvetov_1c_20--odnoletniki_1c_21/nasturtsija_1c_22
+346 1 Кроссандра krossandra_1c2 Кроссандра Кроссандра Кроссандра /nasinnja_kvitiv_1c0--kimnatni_1c1/krossandra_1c2
+301 1 Редиска rediska_1c1 Редиска Редиска Редиска /nasinnja_ovochiv_1c0/rediska_1c1
+344 1 Стреліція strelitsija_1c2 Стреліція Стреліція Стреліція /nasinnja_kvitiv_1c0--kimnatni_1c1/strelitsija_1c2
+465 1 Цикламен tsiklamen_1c2 Цикламен Цикламен Цикламен /nasinnja_kvitiv_1c0--kimnatni_1c1/tsiklamen_1c2
+307 1 Гарбуз garbuz_1c1 Гарбуз Гарбуз Гарбуз /nasinnja_ovochiv_1c0/garbuz_1c1
+308 1 Квасоля kvasolja_1c1 Квасоля Квасоля Квасоля /nasinnja_ovochiv_1c0/kvasolja_1c1
+378 2 Биденс bidens_1c_22 Биденс Биденс Биденс /semena_tsvetov_1c_20--odnoletniki_1c_21/bidens_1c_22
+466 1 Примула primula_1c2 Примула Примула Примула /nasinnja_kvitiv_1c0--bagatorichniki_1c1/primula_1c2
+465 2 Цикламен novaflorex_1c_22 Цикламен Цикламен Цикламен /semena_tsvetov_1c_20--komnatnye_1c_21/novaflorex_1c_22
+415 1 Гербера gerbera_1c2 Гербера Гербера Гербера /nasinnja_kvitiv_1c0--kimnatni_1c1/gerbera_1c2
+421 2 Мимоза mimoza_1c_22 Мимоза Мимоза Мимоза /semena_tsvetov_1c_20--komnatnye_1c_21/mimoza_1c_22
+395 1 Кобея kobeja_1c2 Кобея Кобея Кобея /nasinnja_kvitiv_1c0--odnorichniki_1c1/kobeja_1c2
+420 1 Пасіфлора pasiflora_1c2 Пасіфлора Пасіфлора Пасіфлора /nasinnja_kvitiv_1c0--kimnatni_1c1/pasiflora_1c2
+424 2 Аренария arenarija_1c_22 Аренария Аренария Аренария /semena_tsvetov_1c_20--mnogoletniki_1c_21/arenarija_1c_22
+443 2 Прунелла prunella_1c_22 Прунелла Прунелла Прунелла /semena_tsvetov_1c_20--mnogoletniki_1c_21/prunella_1c_22
+503 1 Садженці троянд sazhentsi_trojand_1c1 Садженці троянд Садженці троянд Садженці троянд /nasinnja_kvitiv_1c0/sazhentsi_trojand_1c1
+472 1 Чорнобривці chornobrivtsi_1c2 Чорнобривці Чорнобривці Чорнобривці /nasinnja_kvitiv_1c0--odnorichniki_1c1/chornobrivtsi_1c2
+482 1 Для розсади dlja_rozsadi_1c1 Для розсади Для розсади Для розсади /tovari_dlja_sadu_ta_gorodu_1c0/dlja_rozsadi_1c1
+414 1 Аспарагус asparagus_1c2 Аспарагус Аспарагус Аспарагус /nasinnja_kvitiv_1c0--kimnatni_1c1/asparagus_1c2
+440 2 Дороникум doronikum_1c_22 Дороникум Дороникум Дороникум /semena_tsvetov_1c_20--mnogoletniki_1c_21/doronikum_1c_22
+322 2 Горох овощной gorokh_ovoshchnoy_1c_21 Горох овощной Горох овощной Горох овощной /semena_ovoshchey_1c_20/gorokh_ovoshchnoy_1c_21
+473 1 Гіацинти (осіння колекція) giatsinti_1c2 Гіацинти (осіння колекція) Гіацинти (осіння колекція) Гіацинти (осіння колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/giatsinti_1c2
+361 2 Армерия armerija_1c_22 Армерия Армерия Армерия /semena_tsvetov_1c_20--mnogoletniki_1c_21/armerija_1c_22
+427 2 Книфофия knifofija_1c_22 Книфофия Книфофия Книфофия /semena_tsvetov_1c_20--mnogoletniki_1c_21/knifofija_1c_22
+504 1 Часник озимий chasnik_ozimiy_1c1 Часник озимий Часник озимий Часник озимий /nasinnja_ovochiv_1c0/chasnik_ozimiy_1c1
+442 2 Морозник selderey_1c_22 Морозник Морозник Морозник /semena_tsvetov_1c_20--mnogoletniki_1c_21/selderey_1c_22
+363 2 Лихнис likhnis_1c_22 Лихнис Лихнис Лихнис /semena_tsvetov_1c_20--mnogoletniki_1c_21/likhnis_1c_22
+428 2 Дельфиниум delfinium_1c_22 Дельфиниум Дельфиниум Дельфиниум /semena_tsvetov_1c_20--mnogoletniki_1c_21/delfinium_1c_22
+370 1 Льон lon_1c2 Льон Льон Льон /nasinnja_kvitiv_1c0--bagatorichniki_1c1/lon_1c2
+446 2 Сальвия salvija_1c_22 Сальвия Сальвия Сальвия /semena_tsvetov_1c_20--mnogoletniki_1c_21/salvija_1c_22
+500 1 Лікарські трави likarski_travi_1c1 Лікарські трави Лікарські трави Лікарські трави /nasinnja_ovochiv_1c0/likarski_travi_1c1
+498 1 Іберіс znjati_z_virobnitstva_1c2 Іберіс Іберіс Іберіс /nasinnja_kvitiv_1c0--bagatorichniki_1c1/znjati_z_virobnitstva_1c2
+417 2 Жакаранда zhakaranda_1c_22 Жакаранда Жакаранда Жакаранда /semena_tsvetov_1c_20--komnatnye_1c_21/zhakaranda_1c_22
+383 1 Гвоздика китайська gvozdika_kitayska_1c2 Гвоздика китайська Гвоздика китайська Гвоздика китайська /nasinnja_kvitiv_1c0--odnorichniki_1c1/gvozdika_kitayska_1c2
+432 2 Гибискус gibiskus_1c_22 Гибискус Гибискус Гибискус /semena_tsvetov_1c_20--mnogoletniki_1c_21/gibiskus_1c_22
+476 1 Тюльпани (осіння колекція) tyulpani_1c2 Тюльпани (осіння колекція) Тюльпани (осіння колекція) Тюльпани (осіння колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/tyulpani_1c2
+471 1 Цибулинні tsibulinni_1c1 Цибулинні Цибулинні Цибулинні /nasinnja_kvitiv_1c0--tsibulinni_1c1
+474 1 Нарциси (осіння колекція) nartsisi_1c2 Нарциси (осіння колекція) Нарциси (осіння колекція) Нарциси (осіння колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/nartsisi_1c2
+477 1 Целозія tselozija_1c2 Целозія Целозія Целозія /nasinnja_kvitiv_1c0--odnorichniki_1c1/tselozija_1c2
+369 1 Цинерарія tsinerarija_1c2 Цинерарія Цинерарія Цинерарія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/tsinerarija_1c2
+470 2 Физостегия fizostegija_1c_22 Физостегия Физостегия Физостегия /semena_tsvetov_1c_20--mnogoletniki_1c_21/fizostegija_1c_22
+441 2 Кореопсис koreopsis_1c_22 Кореопсис Кореопсис Кореопсис /semena_tsvetov_1c_20--mnogoletniki_1c_21/koreopsis_1c_22
+469 1 Статиця переса statitsja_peresax_1c2 Статиця переса Статиця переса Статиця переса /nasinnja_kvitiv_1c0--bagatorichniki_1c1/statitsja_peresax_1c2
+316 2 Серия Elite serija_elite_1c_22 Серия Elite Серия Elite Серия Elite /semena_tsvetov_1c_20--tsvetochnye_smesi_1c_21/serija_elite_1c_22
+490 2 Бегония (весенняя колекция) begonija_vesennjaja_kolektsija_1c_22 Бегония (весенняя колекция) Бегония (весенняя колекция) Бегония (весенняя колекция) /semena_tsvetov_1c_20--lukovichnye_1c_21/begonija_vesennjaja_kolektsija_1c_22
+329 1 Капуста червоноголова kapusta_chervonogolova_1c1 Капуста червоноголова Капуста червоноголова Капуста червоноголова /nasinnja_ovochiv_1c0/kapusta_chervonogolova_1c1
+492 2 Гладиолус (весенняя колекция) gladiolus_vesennjaja_kolektsija_1c_22 Гладиолус (весенняя колекция) Гладиолус (весенняя колекция) Гладиолус (весенняя колекция) /semena_tsvetov_1c_20--lukovichnye_1c_21/gladiolus_vesennjaja_kolektsija_1c_22
+491 2 Георгина (весенняя колекция) georgina_vesennjaja_kolektsija_1c_22 Георгина (весенняя колекция) Георгина (весенняя колекция) Георгина (весенняя колекция) /semena_tsvetov_1c_20--lukovichnye_1c_21/georgina_vesennjaja_kolektsija_1c_22
+406 2 Катарантус katarantus_1c_22 Катарантус Катарантус Катарантус /semena_tsvetov_1c_20--odnoletniki_1c_21/katarantus_1c_22
+335 1 Морква morkva_1c1 Морква Морква Морква /nasinnja_ovochiv_1c0/morkva_1c1
+493 2 Лилия (весенняя колекция) lilija_vesennjaja_kolektsija_1c_22 Лилия (весенняя колекция) Лилия (весенняя колекция) Лилия (весенняя колекция) /semena_tsvetov_1c_20--lukovichnye_1c_21/lilija_vesennjaja_kolektsija_1c_22
+334 1 Огірок ogirok_1c1 Огірок Огірок Огірок /nasinnja_ovochiv_1c0/ogirok_1c1
+494 2 Кала (весенняя колекция) kala_vesennjaja_kolektsija_1c_22 Кала (весенняя колекция) Кала (весенняя колекция) Кала (весенняя колекция) /semena_tsvetov_1c_20--lukovichnye_1c_21/kala_vesennjaja_kolektsija_1c_22
+3 1 Добрива та засоби захисту dobriva_ta_zasobi_zakhistu_1c0 Добрива та засоби захисту Добрива та засоби захисту Добрива та засоби захисту /dobriva_ta_zasobi_zakhistu_1c0
+1 1 Насіння овочів nasinnja_ovochiv_1c0 Насіння овочів Насіння овочів Насіння овочів /nasinnja_ovochiv_1c0
+474 2 Нарциссы nartsissy_1c_22 Нарциссы Нарциссы Нарциссы /semena_tsvetov_1c_20--lukovichnye_1c_21/nartsissy_1c_22
+319 1 Засоби захисту zasobi_zakhistu_1c1 Засоби захисту Засоби захисту Засоби захисту /dobriva_ta_zasobi_zakhistu_1c0/zasobi_zakhistu_1c1
+475 2 Крокусы krokusy_1c_22 Крокусы Крокусы Крокусы /semena_tsvetov_1c_20--lukovichnye_1c_21/krokusy_1c_22
+325 1 Патиссон patisson_1c1 Патиссон Патиссон Патиссон /nasinnja_ovochiv_1c0/patisson_1c1
+489 1 Садовий інвентар sadoviy_inventar_1c1 Садовий інвентар Садовий інвентар Садовий інвентар /tovari_dlja_sadu_ta_gorodu_1c0/sadoviy_inventar_1c1
+327 1 Капуста броколі kapusta_brokoli_1c1 Капуста броколі Капуста броколі Капуста броколі /nasinnja_ovochiv_1c0/kapusta_brokoli_1c1
+479 2 Товары для сада и огорода tovary_dlja_sada_i_ogoroda_1c_20 Товары для сада и огорода Товары для сада и огорода Товары для сада и огорода /tovary_dlja_sada_i_ogoroda_1c_20
+485 2 Перовския perovskija_1c_22 Перовския Перовския Перовския /semena_tsvetov_1c_20--mnogoletniki_1c_21/perovskija_1c_22
+328 1 Капуста брюсельська kapusta_bryuselska_1c1 Капуста брюсельська Капуста брюсельська Капуста брюсельська /nasinnja_ovochiv_1c0/kapusta_bryuselska_1c1
+477 2 Целозия kaktusx_1c_22 Целозия Целозия Целозия /semena_tsvetov_1c_20--odnoletniki_1c_21/kaktusx_1c_22
+341 2 Капуста китайская kapusta_kitayskaja_1c_21 Капуста китайская Капуста китайская Капуста китайская /semena_ovoshchey_1c_20/kapusta_kitayskaja_1c_21
+350 1 Флокс floks_1c2 Флокс Флокс Флокс /nasinnja_kvitiv_1c0--odnorichniki_1c1/floks_1c2
+340 2 Капуста кольраби kapusta_kolrabi_1c_21 Капуста кольраби Капуста кольраби Капуста кольраби /semena_ovoshchey_1c_20/kapusta_kolrabi_1c_21
+302 2 Редька redka_1c_21 Редька Редька Редька /semena_ovoshchey_1c_20/redka_1c_21
+6 2 Биопрепараты biopreparaty_1c_20 Биопрепараты Биопрепараты Биопрепараты /biopreparaty_1c_20
+460 1 Соняшник sonjashnik_1c2 Соняшник Соняшник Соняшник /nasinnja_kvitiv_1c0--odnorichniki_1c1/sonjashnik_1c2
+305 1 Буряк столовий burjak_stoloviy_1c1 Буряк столовий Буряк столовий Буряк столовий /nasinnja_ovochiv_1c0/burjak_stoloviy_1c1
+323 2 Дыня dynja_1c_21 Дыня Дыня Дыня /semena_ovoshchey_1c_20/dynja_1c_21
+324 2 Кабачок kabachok_1c_21 Кабачок Кабачок Кабачок /semena_ovoshchey_1c_20/kabachok_1c_21
+326 2 Капуста белокочанная kapusta_belokochannaja_1c_21 Капуста белокочанная Капуста белокочанная Капуста белокочанная /semena_ovoshchey_1c_20/kapusta_belokochannaja_1c_21
+481 2 Агро-текстиль agrotekstil_1c_21 Агро-текстиль Агро-текстиль Агро-текстиль /tovary_dlja_sada_i_ogoroda_1c_20/agrotekstil_1c_21
+315 1 Насіння газонних трав nasinnja_gazonnikh_trav_1c1 Насіння газонних трав Насіння газонних трав Насіння газонних трав /gazonni_travi_1c0/nasinnja_gazonnikh_trav_1c1
+488 2 Календула kalendula_1c_22 Календула Календула Календула /semena_tsvetov_1c_20--odnoletniki_1c_21/kalendula_1c_22
+327 2 Капуста брокколи kapusta_brokkoli_1c_21 Капуста брокколи Капуста брокколи Капуста брокколи /semena_ovoshchey_1c_20/kapusta_brokkoli_1c_21
+328 2 Капуста брюссельская kapusta_bryusselskaja_1c_21 Капуста брюссельская Капуста брюссельская Капуста брюссельская /semena_ovoshchey_1c_20/kapusta_bryusselskaja_1c_21
+391 2 Георгины georginy_1c_22 Георгины Георгины Георгины /semena_tsvetov_1c_20--odnoletniki_1c_21/georginy_1c_22
+329 2 Капуста краснокочанная kapusta_krasnokochannaja_1c_21 Капуста краснокочанная Капуста краснокочанная Капуста краснокочанная /semena_ovoshchey_1c_20/kapusta_krasnokochannaja_1c_21
+482 2 Для рассады dlja_rassady_1c_21 Для рассады Для рассады Для рассады /tovary_dlja_sada_i_ogoroda_1c_20/dlja_rassady_1c_21
+483 2 Лакфиоль lakfiol_1c_22 Лакфиоль Лакфиоль Лакфиоль /semena_tsvetov_1c_20--dvuletniki_1c_21/lakfiol_1c_22
+401 2 Мирабилис mirabilis_1c_22 Мирабилис Мирабилис Мирабилис /semena_tsvetov_1c_20--odnoletniki_1c_21/mirabilis_1c_22
+5 2 Цветочные смеси tsvetochnye_smesi_1c_21 Цветочные смеси Цветочные смеси Цветочные смеси /semena_tsvetov_1c_20--tsvetochnye_smesi_1c_21
+359 1 Аквілегія akvilegija_1c2 Аквілегія Аквілегія Аквілегія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/akvilegija_1c2
+307 2 Тыква tykva_1c_21 Тыква Тыква Тыква /semena_ovoshchey_1c_20/tykva_1c_21
+407 2 Алиссум alissum_1c_22 Алиссум Алиссум Алиссум /semena_tsvetov_1c_20--mnogoletniki_1c_21/alissum_1c_22
+344 2 Стрелиция strelitsija_1c_22 Стрелиция Стрелиция Стрелиция /semena_tsvetov_1c_20--komnatnye_1c_21/strelitsija_1c_22
+308 2 Фасоль fasol_1c_21 Фасоль Фасоль Фасоль /semena_ovoshchey_1c_20/fasol_1c_21
+309 2 Бобы boby_1c_21 Бобы Бобы Бобы /semena_ovoshchey_1c_20/boby_1c_21
+2 2 Семена цветов semena_tsvetov_1c_20 Семена цветов Семена цветов Семена цветов /semena_tsvetov_1c_20
+311 2 Комнатные komnatnye_1c_21 Комнатные Комнатные Комнатные /semena_tsvetov_1c_20--komnatnye_1c_21
+349 2 Цинния (Майоры) tsinnija_mayory_1c_22 Цинния (Майоры) Цинния (Майоры) Цинния (Майоры) /semena_tsvetov_1c_20--odnoletniki_1c_21/tsinnija_mayory_1c_22
+310 2 Однолетники odnoletniki_1c_21 Однолетники Однолетники Однолетники /semena_tsvetov_1c_20--odnoletniki_1c_21
+412 2 Наперстянка naperstjanka_1c_22 Наперстянка Наперстянка Наперстянка /semena_tsvetov_1c_20--dvuletniki_1c_21/naperstjanka_1c_22
+413 2 Колокольчики kolokolchikix_1c_22 Колокольчики Колокольчики Колокольчики /semena_tsvetov_1c_20--dvuletniki_1c_21/kolokolchikix_1c_22
+314 1 Баклажан baklazhan_1c1 Баклажан Баклажан Баклажан /nasinnja_ovochiv_1c0/baklazhan_1c1
+429 2 Арабис arabis_1c_22 Арабис Арабис Арабис /semena_tsvetov_1c_20--mnogoletniki_1c_21/arabis_1c_22
+389 2 Эустома eustoma_1c_22 Эустома Эустома Эустома /semena_tsvetov_1c_20--odnoletniki_1c_21/eustoma_1c_22
+321 2 Арбуз arbuz_1c_21 Арбуз Арбуз Арбуз /semena_ovoshchey_1c_20/arbuz_1c_21
+317 2 Novaflore novaflore_1c_22 Novaflore Novaflore Novaflore /semena_tsvetov_1c_20--tsvetochnye_smesi_1c_21/novaflore_1c_22
+358 2 Астра альпийская astra_alpiyskaja_1c_22 Астра альпийская Астра альпийская Астра альпийская /semena_tsvetov_1c_20--mnogoletniki_1c_21/astra_alpiyskaja_1c_22
+339 2 Капуста савойская kapusta_savoyskaja_1c_21 Капуста савойская Капуста савойская Капуста савойская /semena_ovoshchey_1c_20/kapusta_savoyskaja_1c_21
+414 2 Аспарагус asparagus_1c_22 Аспарагус Аспарагус Аспарагус /semena_tsvetov_1c_20--komnatnye_1c_21/asparagus_1c_22
+338 2 Капуста цветная kapusta_tsvetnaja_1c_21 Капуста цветная Капуста цветная Капуста цветная /semena_ovoshchey_1c_20/kapusta_tsvetnaja_1c_21
+304 2 Салатный микс salatnyy_miks_1c_21 Салатный микс Салатный микс Салатный микс /semena_ovoshchey_1c_20/salatnyy_miks_1c_21
+370 2 Лен len_1c_22 Лен Лен Лен /semena_tsvetov_1c_20--mnogoletniki_1c_21/len_1c_22
+306 2 Томат tomat_1c_21 Томат Томат Томат /semena_ovoshchey_1c_20/tomat_1c_21
+450 2 Лобелия lobelija_1c_22 Лобелия Лобелия Лобелия /semena_tsvetov_1c_20--odnoletniki_1c_21/lobelija_1c_22
+318 2 Удобрения udobrenija_1c_21 Удобрения Удобрения Удобрения /udobrenija_i_sredstva_zashchity_1c_20/udobrenija_1c_21
+392 2 Ипомея ipomeja_1c_22 Ипомея Ипомея Ипомея /semena_tsvetov_1c_20--odnoletniki_1c_21/ipomeja_1c_22
+375 2 Астра astra_1c_22 Астра Астра Астра /semena_tsvetov_1c_20--odnoletniki_1c_21/astra_1c_22
+325 2 Патиссон patisson_1c_21 Патиссон Патиссон Патиссон /semena_ovoshchey_1c_20/patisson_1c_21
+379 2 Брахикома brakhikoma_1c_22 Брахикома Брахикома Брахикома /semena_tsvetov_1c_20--odnoletniki_1c_21/brakhikoma_1c_22
+305 2 Свекла столовая svekla_stolovaja_1c_21 Свекла столовая Свекла столовая Свекла столовая /semena_ovoshchey_1c_20/svekla_stolovaja_1c_21
+377 2 Бессмертник Гелихризум bessmertnik_gelikhrizum_1c_22 Бессмертник Гелихризум Бессмертник Гелихризум Бессмертник Гелихризум /semena_tsvetov_1c_20--odnoletniki_1c_21/bessmertnik_gelikhrizum_1c_22
+471 2 Луковичные lukovichnye_1c_21 Луковичные Луковичные Луковичные /semena_tsvetov_1c_20--lukovichnye_1c_21
+355 2 Мак mak_1c_22 Мак Мак Мак /semena_tsvetov_1c_20--odnoletniki_1c_21/mak_1c_22
+393 2 Кохия kokhija_1c_22 Кохия Кохия Кохия /semena_tsvetov_1c_20--odnoletniki_1c_21/kokhija_1c_22
+448 2 Бальзамин redkax_1c_22 Бальзамин Бальзамин Бальзамин /semena_tsvetov_1c_20--odnoletniki_1c_21/redkax_1c_22
+382 2 Газания gazanija_1c_22 Газания Газания Газания /semena_tsvetov_1c_20--odnoletniki_1c_21/gazanija_1c_22
+458 2 Портулак portulak_1c_22 Портулак Портулак Портулак /semena_tsvetov_1c_20--odnoletniki_1c_21/portulak_1c_22
+459 2 Сальвия salvijax_1c_22 Сальвия Сальвия Сальвия /semena_tsvetov_1c_20--odnoletniki_1c_21/salvijax_1c_22
+381 2 Василек vasilek_1c_22 Василек Василек Василек /semena_tsvetov_1c_20--odnoletniki_1c_21/vasilek_1c_22
+380 2 Вербена verbena_1c_22 Вербена Вербена Вербена /semena_tsvetov_1c_20--odnoletniki_1c_21/verbena_1c_22
+353 2 Львиный зев (Антирринум) antirrinum_lvinyy_zev_1c_22 Львиный зев (Антирринум) Львиный зев (Антирринум) Львиный зев (Антирринум) /semena_tsvetov_1c_20--odnoletniki_1c_21/antirrinum_lvinyy_zev_1c_22
+371 2 Петуния petunija_1c_22 Петуния Петуния Петуния /semena_tsvetov_1c_20--odnoletniki_1c_21/petunija_1c_22
+372 2 Люпин lyupin_1c_22 Люпин Люпин Люпин /semena_tsvetov_1c_20--mnogoletniki_1c_21/lyupin_1c_22
+332 2 Пряные и зеленые культуры prjanye_i_zelenye_kultury_1c_21 Пряные и зеленые культуры Пряные и зеленые культуры Пряные и зеленые культуры /semena_ovoshchey_1c_20/prjanye_i_zelenye_kultury_1c_21
+331 2 Сельдерей selderey_1c_21 Сельдерей Сельдерей Сельдерей /semena_ovoshchey_1c_20/selderey_1c_21
+301 2 Редис redis_1c_21 Редис Редис Редис /semena_ovoshchey_1c_20/redis_1c_21
+348 1 Бегонія begonija_1c2 Бегонія Бегонія Бегонія /nasinnja_kvitiv_1c0--kimnatni_1c1/begonija_1c2
+415 2 Гербера gerbera_1c_22 Гербера Гербера Гербера /semena_tsvetov_1c_20--komnatnye_1c_21/gerbera_1c_22
+356 2 Виола viola_1c_22 Виола Виола Виола /semena_tsvetov_1c_20--dvuletniki_1c_21/viola_1c_22
+493 1 Лілія (весняна колекція) lilija_vesnjana_kolektsija_1c2 Лілія (весняна колекція) Лілія (весняна колекція) Лілія (весняна колекція) /nasinnja_kvitiv_1c0--tsibulinni_1c1/lilija_vesnjana_kolektsija_1c2
+348 2 Бегония begonija_1c_22 Бегония Бегония Бегония /semena_tsvetov_1c_20--komnatnye_1c_21/begonija_1c_22
+349 1 Цинія (Майори) tsinija_mayori_1c2 Цинія (Майори) Цинія (Майори) Цинія (Майори) /nasinnja_kvitiv_1c0--odnorichniki_1c1/tsinija_mayori_1c2
+360 2 Альстремерия alstremerija_1c_22 Альстремерия Альстремерия Альстремерия /semena_tsvetov_1c_20--mnogoletniki_1c_21/alstremerija_1c_22
+478 1 Лобулярія (Аліссум) lobuljarija_alissum_1c2 Лобулярія (Аліссум) Лобулярія (Аліссум) Лобулярія (Аліссум) /nasinnja_kvitiv_1c0--odnorichniki_1c1/lobuljarija_alissum_1c2
+347 2 Банан banan_1c_22 Банан Банан Банан /semena_tsvetov_1c_20--komnatnye_1c_21/banan_1c_22
+330 2 Капуста пекинская kapusta_pekinskaja_1c_21 Капуста пекинская Капуста пекинская Капуста пекинская /semena_ovoshchey_1c_20/kapusta_pekinskaja_1c_21
+337 2 Кукуруза сахарная kukuruza_sakharnaja_1c_21 Кукуруза сахарная Кукуруза сахарная Кукуруза сахарная /semena_ovoshchey_1c_20/kukuruza_sakharnaja_1c_21
+346 2 Кроссандра krossandra_1c_22 Кроссандра Кроссандра Кроссандра /semena_tsvetov_1c_20--komnatnye_1c_21/krossandra_1c_22
+336 2 Лук luk_1c_21 Лук Лук Лук /semena_ovoshchey_1c_20/luk_1c_21
+335 2 Морковь morkov_1c_21 Морковь Морковь Морковь /semena_ovoshchey_1c_20/morkov_1c_21
+334 2 Огурец ogurets_1c_21 Огурец Огурец Огурец /semena_ovoshchey_1c_20/ogurets_1c_21
+364 1 Ломикамінь (Камнеломка) lomikamin_kamnelomka_1c2 Ломикамінь (Камнеломка) Ломикамінь (Камнеломка) Ломикамінь (Камнеломка) /nasinnja_kvitiv_1c0--bagatorichniki_1c1/lomikamin_kamnelomka_1c2
+333 2 Перец perets_1c_21 Перец Перец Перец /semena_ovoshchey_1c_20/perets_1c_21
+315 2 Семена газонных трав semena_gazonnykh_trav_1c_21 Семена газонных трав Семена газонных трав Семена газонных трав /gazonnye_travy_1c_20/semena_gazonnykh_trav_1c_21
+447 2 Асклепиас asklepias_1c_22 Асклепиас Асклепиас Асклепиас /semena_tsvetov_1c_20--mnogoletniki_1c_21/asklepias_1c_22
+4 2 Газонные травы gazonnye_travy_1c_20 Газонные травы Газонные травы Газонные травы /gazonnye_travy_1c_20
+319 2 Средства защиты sredstva_zashchity_1c_21 Средства защиты Средства защиты Средства защиты /udobrenija_i_sredstva_zashchity_1c_20/sredstva_zashchity_1c_21
+460 2 Подсолнух podsolnechnik_1c_22 Подсолнух Подсолнух Подсолнух /semena_tsvetov_1c_20--odnoletniki_1c_21/podsolnechnik_1c_22
+387 2 Горошек goroshek_1c_22 Горошек Горошек Горошек /semena_tsvetov_1c_20--odnoletniki_1c_21/goroshek_1c_22
+352 2 Статица statitsa_1c_22 Статица Статица Статица /semena_tsvetov_1c_20--odnoletniki_1c_21/statitsa_1c_22
+3 2 Удобрения и средства защиты udobrenija_i_sredstva_zashchity_1c_20 Удобрения и средства защиты Удобрения и средства защиты Удобрения и средства защиты /udobrenija_i_sredstva_zashchity_1c_20
+424 1 Аренарія arenarija_1c2 Аренарія Аренарія Аренарія /nasinnja_kvitiv_1c0--bagatorichniki_1c1/arenarija_1c2
+303 2 Салат salat_1c_21 Салат Салат Салат /semena_ovoshchey_1c_20/salat_1c_21
+444 2 Рудбекия rudbekija_1c_22 Рудбекия Рудбекия Рудбекия /semena_tsvetov_1c_20--odnoletniki_1c_21/rudbekija_1c_22
+473 2 Гиацинты giatsinty_1c_22 Гиацинты Гиацинты Гиацинты /semena_tsvetov_1c_20--lukovichnye_1c_21/giatsinty_1c_22
+468 1 Дзвоники dzvonikix_1c2 Дзвоники Дзвоники Дзвоники /nasinnja_kvitiv_1c0--bagatorichniki_1c1/dzvonikix_1c2
+1 2 Семена овощей semena_ovoshchey_1c_20 Семена овощей Семена овощей Семена овощей /semena_ovoshchey_1c_20
+388 2 Дихондра dikhondra_1c_22 Дихондра Дихондра Дихондра /semena_tsvetov_1c_20--odnoletniki_1c_21/dikhondra_1c_22
+418 2 Кофейное дерево kofeynoe_derevo_1c_22 Кофейное дерево Кофейное дерево Кофейное дерево /semena_tsvetov_1c_20--komnatnye_1c_21/kofeynoe_derevo_1c_22
+478 2 Лобулярия (Алиссум) lobuljarija_alissum_1c_22 Лобулярия (Алиссум) Лобулярия (Алиссум) Лобулярия (Алиссум) /semena_tsvetov_1c_20--odnoletniki_1c_21/lobuljarija_alissum_1c_22
+504 2 Чеснок озимый chasnik_ozimiy_1c_21 Чеснок озимый Чеснок озимый Чеснок озимый /semena_ovoshchey_1c_20/chasnik_ozimiy_1c_21
+395 2 Кобея kobeja_1c_22 Кобея Кобея Кобея /semena_tsvetov_1c_20--odnoletniki_1c_21/kobeja_1c_22
+503 2 Саженцы роз sazhentsi_trojand_1c_21 Саженцы роз Саженцы роз Саженцы роз /semena_tsvetov_1c_20/sazhentsi_trojand_1c_21
+498 2 Иберис iberis_1c_22 Иберис Иберис Иберис /semena_tsvetov_1c_20--mnogoletniki_1c_21/iberis_1c_22
+350 2 Флокс floks_1c_22 Флокс Флокс Флокс /semena_tsvetov_1c_20--odnoletniki_1c_21/floks_1c_22
+312 2 Двулетники dvuletniki_1c_21 Двулетники Двулетники Двулетники /semena_tsvetov_1c_20--dvuletniki_1c_21
+364 2 Камнеломка kamnelomka_1c_22 Камнеломка Камнеломка Камнеломка /semena_tsvetov_1c_20--mnogoletniki_1c_21/kamnelomka_1c_22
+409 2 Мальва malva_1c_22 Мальва Мальва Мальва /semena_tsvetov_1c_20--dvuletniki_1c_21/malva_1c_22
+313 2 Многолетники mnogoletniki_1c_21 Многолетники Многолетники Многолетники /semena_tsvetov_1c_20--mnogoletniki_1c_21
+365 2 Обриета obrieta_1c_22 Обриета Обриета Обриета /semena_tsvetov_1c_20--mnogoletniki_1c_21/obrieta_1c_22
+404 2 Ягодные jagodnye_1c_21 Ягодные Ягодные Ягодные /semena_tsvetov_1c_20/jagodnye_1c_21
+369 2 Цинерария tsinerarija_1c_22 Цинерария Цинерария Цинерария /semena_tsvetov_1c_20--mnogoletniki_1c_21/tsinerarija_1c_22
+367 2 Ромашка romashka_1c_22 Ромашка Ромашка Ромашка /semena_tsvetov_1c_20--mnogoletniki_1c_21/romashka_1c_22
+359 2 Аквилегия akvilegija_1c_22 Аквилегия Аквилегия Аквилегия /semena_tsvetov_1c_20--mnogoletniki_1c_21/akvilegija_1c_22
+410 2 Гвоздика gvozdika_1c_22 Гвоздика Гвоздика Гвоздика /semena_tsvetov_1c_20--dvuletniki_1c_21/gvozdika_1c_22
+362 2 Гипсофила kachim_gypsophila_1c_22 Гипсофила Гипсофила Гипсофила /semena_tsvetov_1c_20--mnogoletniki_1c_21/kachim_gypsophila_1c_22
+314 2 Баклажан baklazhan_1c_21 Баклажан Баклажан Баклажан /semena_ovoshchey_1c_20/baklazhan_1c_21
+500 2 Лекарственные травы likarskie_travu_1c_21 Лекарственные травы Лекарственные травы Лекарственные травы /semena_ovoshchey_1c_20/likarskie_travu_1c_21
+405 2 Гвоздика gvozdikax_1c_22 Гвоздика Гвоздика Гвоздика /semena_tsvetov_1c_20--mnogoletniki_1c_21/gvozdikax_1c_22
+469 2 Статица Переса statitsja_peresax_1c_22 Статица Переса Статица Переса Статица Переса /semena_tsvetov_1c_20--mnogoletniki_1c_21/statitsja_peresax_1c_22
+345 2 Кактус kaktus_1c_22 Кактус Кактус Кактус /semena_tsvetov_1c_20--komnatnye_1c_21/kaktus_1c_22
+476 2 Тюльпаны tyulpany_1c_22 Тюльпаны Тюльпаны Тюльпаны /semena_tsvetov_1c_20--lukovichnye_1c_21/tyulpany_1c_22
+368 2 Сон-трава sontrava_1c_22 Сон-трава Сон-трава Сон-трава /semena_tsvetov_1c_20--mnogoletniki_1c_21/sontrava_1c_22
+420 2 Пассифлора pasiflora_1c_22 Пассифлора Пассифлора Пассифлора /semena_tsvetov_1c_20--komnatnye_1c_21/pasiflora_1c_22
+312 1 Дворічники dvorichniki_1c1 Дворічники Дворічники Дворічники /nasinnja_kvitiv_1c0--dvorichniki_1c1
+\.
+
+
+--
+-- TOC entry 2627 (class 0 OID 0)
+-- Dependencies: 187
+-- Name: catalog_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('catalog_id_seq', 508, true);
+
+
+--
+-- TOC entry 2540 (class 0 OID 83741)
+-- Dependencies: 172
+-- Data for Name: language; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY language (language_id, language_code, is_default, language_name, status, country_code) FROM stdin;
+1 uk 0 Українська 1 ua
+2 ru 1 Русский 1 ru
+3 en 0 English 0 us
+4 fr 0 Français 0 fr
+5 lt 0 Lietùvių 0 lt
+6 lv 0 Latviešu 0 lv
+7 de 0 Deutsch 0 de
+0 df 0 Default 1 df
+\.
+
+
+--
+-- TOC entry 2541 (class 0 OID 83746)
+-- Dependencies: 173
+-- Data for Name: language_lang; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY language_lang (language_id, lang_title, lang_id) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2628 (class 0 OID 0)
+-- Dependencies: 174
+-- Name: language_language_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('language_language_id_seq', 7, true);
+
+
+--
+-- TOC entry 2577 (class 0 OID 84091)
+-- Dependencies: 209
+-- Data for Name: media; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY media (media_id, hash, extension) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2629 (class 0 OID 0)
+-- Dependencies: 221
+-- Name: media_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('media_id_seq', 1, true);
+
+
+--
+-- TOC entry 2543 (class 0 OID 83751)
+-- Dependencies: 175
+-- Data for Name: menu; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY menu (menu_id, menu_pid, level, termin_id, show, is_open, menu_location_id, sortorder, name, url) FROM stdin;
+1 0 0 2 1 0 1 12 \N \N
+2 1 1 3 1 0 1 1 \N \N
+3 1 1 4 1 0 1 1 \N \N
+4 0 0 5 1 0 1 1 \N \N
+5 1 1 6 1 0 1 1 \N \N
+6 1 1 7 1 0 1 1 \N \N
+7 0 0 8 1 0 1 1 \N \N
+8 0 0 9 1 0 1 1 \N \N
+9 0 0 10 1 0 1 1 \N \N
+10 0 0 11 1 0 1 1 \N \N
+11 0 0 12 1 0 1 1 \N \N
+12 0 0 13 1 0 1 1 \N \N
+\.
+
+
+--
+-- TOC entry 2544 (class 0 OID 83759)
+-- Dependencies: 176
+-- Data for Name: menu_location; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY menu_location (menu_location_id, menu_location_name) FROM stdin;
+1 TOP
+2 CENTER
+3 BOTTOM
+\.
+
+
+--
+-- TOC entry 2545 (class 0 OID 83762)
+-- Dependencies: 177
+-- Data for Name: menu_location_lang; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY menu_location_lang (menu_location_id, menu_location_title, lang_id) FROM stdin;
+1 Верхнее 2
+2 Центр 2
+3 Низ 2
+\.
+
+
+--
+-- TOC entry 2578 (class 0 OID 84097)
+-- Dependencies: 210
+-- Data for Name: option; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY option (model, option_id, model_id, name, template, option_pid, translate, date_add) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2630 (class 0 OID 0)
+-- Dependencies: 219
+-- Name: option_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('option_id_seq', 1, true);
+
+
+--
+-- TOC entry 2579 (class 0 OID 84108)
+-- Dependencies: 211
+-- Data for Name: option_lang; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY option_lang (option_lang_id, option_id, language_id, value) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2631 (class 0 OID 0)
+-- Dependencies: 220
+-- Name: option_lang_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('option_lang_id_seq', 1, true);
+
+
+--
+-- TOC entry 2569 (class 0 OID 83963)
+-- Dependencies: 201
+-- Data for Name: social; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY social (social_name, social_user_id, user_id, social_id) FROM stdin;
+vkontakte 63440239 13 1
+\.
+
+
+--
+-- TOC entry 2632 (class 0 OID 0)
+-- Dependencies: 202
+-- Name: social_social_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('social_social_id_seq', 1, true);
+
+
+--
+-- TOC entry 2546 (class 0 OID 83765)
+-- Dependencies: 178
+-- Data for Name: termin; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY termin (termin_id, termin_name, is_book) FROM stdin;
+4 \N 0
+5 \N 0
+6 \N 0
+7 \N 0
+9 \N 0
+10 \N 0
+11 \N 0
+12 \N 0
+13 \N 0
+14 \N 0
+15 \N 0
+16 \N 0
+20 \N 0
+321 \N 0
+322 \N 0
+323 \N 0
+324 \N 0
+325 \N 0
+326 \N 0
+327 \N 0
+328 \N 0
+329 \N 0
+330 \N 0
+331 \N 0
+332 \N 0
+333 \N 0
+334 \N 0
+335 \N 0
+336 \N 0
+337 \N 0
+338 \N 0
+339 \N 0
+340 \N 0
+341 \N 0
+342 \N 0
+343 \N 0
+344 \N 0
+345 \N 0
+346 \N 0
+347 \N 0
+348 \N 0
+349 \N 0
+350 \N 0
+351 \N 0
+352 \N 0
+353 \N 0
+354 \N 0
+355 \N 0
+356 \N 0
+357 \N 0
+358 \N 0
+359 \N 0
+360 \N 0
+361 \N 0
+362 \N 0
+363 \N 0
+364 \N 0
+365 \N 0
+366 \N 0
+367 \N 0
+368 \N 0
+369 \N 0
+370 \N 0
+371 \N 0
+372 \N 0
+373 \N 0
+374 \N 0
+375 \N 0
+376 \N 0
+377 \N 0
+378 \N 0
+379 \N 0
+380 \N 0
+381 \N 0
+382 \N 0
+383 \N 0
+384 \N 0
+385 \N 0
+386 \N 0
+387 \N 0
+388 \N 0
+389 \N 0
+390 \N 0
+391 \N 0
+392 \N 0
+393 \N 0
+394 \N 0
+395 \N 0
+396 \N 0
+397 \N 0
+398 \N 0
+399 \N 0
+400 \N 0
+401 \N 0
+402 \N 0
+403 \N 0
+404 \N 0
+405 \N 0
+406 \N 0
+407 \N 0
+408 \N 0
+409 \N 0
+410 \N 0
+411 \N 0
+412 \N 0
+413 \N 0
+414 \N 0
+415 \N 0
+416 \N 0
+417 \N 0
+418 \N 0
+419 \N 0
+420 \N 0
+421 \N 0
+422 \N 0
+423 \N 0
+424 \N 0
+425 \N 0
+426 \N 0
+427 \N 0
+428 \N 0
+429 \N 0
+430 \N 0
+431 \N 0
+432 \N 0
+433 \N 0
+434 \N 0
+435 \N 0
+436 \N 0
+437 \N 0
+438 \N 0
+439 \N 0
+440 \N 0
+441 \N 0
+442 \N 0
+443 \N 0
+444 \N 0
+530 \N 0
+445 \N 0
+446 \N 0
+447 \N 0
+448 \N 0
+449 \N 0
+450 \N 0
+451 \N 0
+452 \N 0
+453 \N 0
+454 \N 0
+455 \N 0
+456 \N 0
+457 \N 0
+458 \N 0
+459 \N 0
+460 \N 0
+461 \N 0
+462 \N 0
+463 \N 0
+464 \N 0
+465 \N 0
+466 \N 0
+467 \N 0
+468 \N 0
+469 \N 0
+470 \N 0
+471 \N 0
+472 \N 0
+473 \N 0
+474 \N 0
+475 \N 0
+476 \N 0
+477 \N 0
+478 \N 0
+479 \N 0
+480 \N 0
+481 \N 0
+482 \N 0
+483 \N 0
+484 \N 0
+485 \N 0
+486 \N 0
+487 \N 0
+488 \N 0
+489 \N 0
+490 \N 0
+491 \N 0
+492 \N 0
+493 \N 0
+494 \N 0
+495 \N 0
+496 \N 0
+497 \N 0
+498 \N 0
+499 \N 0
+500 \N 0
+501 \N 0
+502 \N 0
+503 \N 0
+504 \N 0
+505 \N 0
+506 \N 0
+507 \N 0
+508 \N 0
+509 \N 0
+510 \N 0
+511 \N 0
+512 \N 0
+513 \N 0
+514 \N 0
+515 \N 0
+516 \N 0
+517 \N 0
+518 \N 0
+519 \N 0
+520 \N 0
+521 \N 0
+522 \N 0
+523 \N 0
+524 \N 0
+525 \N 0
+526 \N 0
+527 \N 0
+528 \N 0
+529 \N 0
+2 \N 0
+3 \N 0
+531 \N 0
+532 \N 0
+533 \N 0
+534 \N 0
+535 \N 0
+536 \N 0
+537 \N 0
+538 \N 0
+539 \N 0
+540 \N 0
+541 \N 0
+542 \N 0
+543 \N 0
+544 \N 0
+545 \N 0
+546 \N 0
+547 \N 0
+548 \N 0
+549 \N 0
+550 \N 0
+551 \N 0
+552 \N 0
+553 \N 0
+554 \N 0
+555 \N 0
+556 \N 0
+557 \N 0
+558 \N 0
+559 \N 0
+560 \N 0
+561 \N 0
+562 \N 0
+563 \N 0
+564 \N 0
+565 \N 0
+566 \N 0
+567 \N 0
+568 \N 0
+569 \N 0
+570 \N 0
+571 \N 0
+572 \N 0
+573 \N 0
+574 \N 0
+575 \N 0
+576 \N 0
+577 \N 0
+578 \N 0
+579 \N 0
+580 \N 0
+581 \N 0
+582 \N 0
+583 \N 0
+584 \N 0
+585 \N 0
+586 \N 0
+587 \N 0
+588 \N 0
+589 \N 0
+590 \N 0
+591 \N 0
+592 \N 0
+593 \N 0
+594 \N 0
+595 \N 0
+596 \N 0
+597 \N 0
+598 \N 0
+599 \N 0
+600 \N 0
+601 \N 0
+602 \N 0
+603 \N 0
+604 \N 0
+605 \N 0
+606 \N 0
+607 \N 0
+608 \N 0
+609 \N 0
+610 \N 0
+611 \N 0
+612 \N 0
+613 \N 0
+614 \N 0
+615 \N 0
+1 \N 0
+8 SHOP 1
+22 PAGE 1
+24 NEWS 1
+\.
+
+
+--
+-- TOC entry 2556 (class 0 OID 83881)
+-- Dependencies: 188
+-- Data for Name: termin_book; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY termin_book (termin_book_id, termin_book_name, sortorder) FROM stdin;
+3 NEWS 3
+1 PAGE 1
+2 SHOP 2
+\.
+
+
+--
+-- TOC entry 2633 (class 0 OID 0)
+-- Dependencies: 189
+-- Name: termin_book_book_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('termin_book_book_id_seq', 3, true);
+
+
+--
+-- TOC entry 2558 (class 0 OID 83886)
+-- Dependencies: 190
+-- Data for Name: termin_book_lang; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY termin_book_lang (termin_book_id, lang_id, termin_book_title) FROM stdin;
+2 2 Магазин: категории
+1 2 Страницы
+3 2 Новости
+\.
+
+
+--
+-- TOC entry 2547 (class 0 OID 83768)
+-- Dependencies: 179
+-- Data for Name: termin_lang; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY termin_lang (termin_id, language_id, termin_title, termin_alias) FROM stdin;
+1 2 Главная /
+2 2 Контакты contacts
+4 2 О компании about
+5 2 Условия использования use
+6 2 Условия сотрудничества corporation
+7 2 Магазины shops
+9 2 Оплата payment
+10 2 Доставка delivery
+11 2 Гарантия warranty
+12 2 Акции promotion
+13 2 Материалы/блог news
+14 2 Авторизация login
+15 2 Выход logout
+16 2 Регистрация signup
+3 2 Поставщики vendors
+8 2 Магазин: каталог catalog
+20 2 Семена semena
+321 2 Средства защиты растений sredstva-zaschity-rasteniy
+322 2 Для борьбы с вредителями dlya-borby-s-vreditelyami
+323 2 Мицелий грибов miceliy-gribov
+324 2 Шиитаке shiitake
+325 2 Цветочные, Комнатные cvetochnye-komnatnye
+326 2 Абутилон abutilon
+327 2 Мимоза mimoza
+328 2 Хлопчатник hlopchatnik
+329 2 Цикламен ciklamen
+330 2 Цветочные cvetochnye
+331 2 Агератум ageratum
+332 2 Агростемма agrostemma
+333 2 Адонис adonis
+334 2 Аквилегия akvilegiya
+335 2 Алиссум alissum
+336 2 Альстромерия alstromeriya
+337 2 Амарант amarant
+338 2 Аммобиум ammobium
+339 2 Анагаллис anagallis
+340 2 Арктотис arktotis
+341 2 Астра astra
+342 2 Бальзамин balzamin
+343 2 Барвинок barvinok
+344 2 Бартония bartoniya
+345 2 Бархатцы barhatcy
+346 2 Бегония begoniya
+347 2 Брахикома brahikoma
+348 2 Бровалия brovaliya
+349 2 Буддлея buddleya
+350 2 Василек vasilek
+351 2 Ваточник vatochnik
+352 2 Венидиум venidium
+353 2 Вербена verbena
+354 2 Вероника veronika
+355 2 Виола viola
+356 2 Вязель vyazel
+357 2 Вьюнок vyunok
+358 2 Газания gazaniya
+359 2 Гайлардия gaylardiya
+360 2 Гвоздика gvozdika
+361 2 Гелиптерум gelipterum
+362 2 Гелихризум gelihrizum
+363 2 Георгина georgina
+364 2 Гиацинтовые бобы giacintovye-boby
+365 2 Гибискус gibiskus
+366 2 Гипсофила gipsofila
+367 2 Годеция godeciya
+368 2 Гомфрена gomfrena
+369 2 Дельфиниум delfinium
+370 2 Диморфотека dimorfoteka
+371 2 Дурман durman
+372 2 Душистый горошек dushistyy-goroshek
+373 2 Душистый табак dushistyy-tabak
+374 2 Дюшенея dyusheneya
+375 2 Зайцехвостник zaycehvostnik
+376 2 Иберис iberis
+377 2 Инкарвиллея inkarvilleya
+378 2 Ипомея ipomeya
+379 2 Кактус kaktus
+380 2 Календула kalendula
+381 2 Квамоклит kvamoklit
+382 2 Кларкия klarkiya
+383 2 Клематис klematis
+384 2 Клеома kleoma
+385 2 Кобея kobeya
+386 2 Колокольчик kolokolchik
+387 2 Кореопсис koreopsis
+388 2 Космея kosmeya
+389 2 Лаватера lavatera
+390 2 Лагерстрёмия lagerstremiya
+391 2 Лапчатка lapchatka
+392 2 Левкой levkoy
+393 2 Лен len
+394 2 Лиатрис liatris
+395 2 Лилейник lileynik
+396 2 Лимониум limonium
+397 2 Лихнис lihnis
+398 2 Лобелия lobeliya
+399 2 Лунник lunnik
+400 2 Люпин lyupin
+401 2 Львиный зев lvinyy-zev
+402 2 Льнянка lnyanka
+403 2 Мак mak
+404 2 Малопа malopa
+405 2 Мальва malva
+406 2 Маргаритка margaritka
+407 2 Маттиола mattiola
+408 2 Мезембриантемум mezembriantemum
+409 2 Мимулюс mimulyus
+410 2 Мирабилис mirabilis
+411 2 Молочай molochay
+412 2 Молюцелла molyucella
+413 2 Мыльнянка mylnyanka
+414 2 Наперстянка naperstyanka
+415 2 Настурция nasturciya
+416 2 Незабудка nezabudka
+417 2 Немезия nemeziya
+418 2 Немофила nemofila
+419 2 Нигелла nigella
+420 2 Нолана nolana
+421 2 Ночная фиалка nochnaya-fialka
+422 2 Обриета obrieta
+423 2 Пассифлора passiflora
+424 2 Пеларгония pelargoniya
+425 2 Пенстемон penstemon
+426 2 Петуния petuniya
+427 2 Пиретрум piretrum
+428 2 Подсолнух podsolnuh
+429 2 Портулак portulak
+430 2 Примула primula
+431 2 Ратибида ratibida
+432 2 Резеда rezeda
+433 2 Резуха rezuha
+434 2 Ромашка romashka
+435 2 Рудбекия rudbekiya
+436 2 Сальвия salviya
+437 2 Сальпиглоссис salpiglossis
+438 2 Симфиандра simfiandra
+439 2 Скабиоза skabioza
+440 2 Смолевка smolevka
+441 2 Смолка smolka
+442 2 Солнцецвет solncecvet
+443 2 Статица statica
+444 2 Стрелитция strelitciya
+445 2 Сурфиния surfiniya
+446 2 Сухоцветник suhocvetnik
+447 2 Схизантус shizantus
+448 2 Трясунка tryasunka
+449 2 Тунбергия tunbergiya
+450 2 Тысячелистник tysyachelistnik
+451 2 Флокс floks
+452 2 Хейрантус heyrantus
+453 2 Хризантема hrizantema
+454 2 Смесь smes
+455 2 Целозия celoziya
+456 2 Цинерария cinerariya
+457 2 Цинния cinniya
+458 2 Черноголовка chernogolovka
+459 2 Чина china
+460 2 Эдельвейс edelveys
+461 2 Энотера enotera
+462 2 Эригерон erigeron
+463 2 Эхинацея ehinaceya
+464 2 Эхиум ehium
+465 2 Эшшольция eshsholciya
+466 2 Ясколка yaskolka
+467 2 Удобрения udobreniya
+468 2 Хелатные удобрения, Регуляторы роста helatnye-udobreniya-regulyatory-rosta
+469 2 Хелатные удобрения helatnye-udobreniya
+470 2 Сопутствующие товары soputstvuyuschie-tovary
+471 2 Упаковочные материал upakovochnye-material
+472 2 Укрывной материал ukryvnoy-material
+473 2 Трутовик trutovik
+474 2 Грунтосмесь gruntosmes
+475 2 Субстрат substrat
+476 2 Сетки setki
+477 2 разрыхлитель razryhlitel
+478 2 Пряные, Лекарственные pryanye-lekarstvennye
+479 2 Душица (материнка) dushica-materinka
+480 2 Иссоп issop
+481 2 Кервель kervel
+482 2 Мелисса melissa
+483 2 Монарда monarda
+484 2 Мята myata
+485 2 Рута ruta
+486 2 Тимьян timyan
+487 2 Шалфей shalfey
+488 2 Пряные, Комнатные pryanye-komnatnye
+489 2 Майоран mayoran
+490 2 Огуречная трава ogurechnaya-trava
+491 2 Розмарин rozmarin
+492 2 Пряные, Зеленые pryanye-zelenye
+493 2 Базилик bazilik
+494 2 Перилла perilla
+495 2 Туласи tulasi
+496 2 Чабер chaber
+497 2 Черемша cheremsha
+498 2 Эстрагон estragon
+499 2 Трава для кошек trava-dlya-koshek
+500 2 Кориандр koriandr
+501 2 Котовник kotovnik
+502 2 Пряные pryanye
+503 2 Анис anis
+504 2 Горчица gorchica
+505 2 Грибная трава gribnaya-trava
+506 2 Тмин tmin
+507 2 Протравители protraviteli
+508 2 Биопрепараты biopreparaty
+509 2 Органические удобрения organicheskie-udobreniya
+510 2 Опёнок openok
+511 2 Овощные, Пряные ovoschnye-pryanye
+512 2 Овсяный корень ovsyanyy-koren
+513 2 Пастернак pasternak
+514 2 Овощные, Зеленые ovoschnye-zelenye
+515 2 Лук luk
+516 2 Мангольд mangold
+517 2 Петрушка petrushka
+518 2 Ревень reven
+519 2 Салат salat
+520 2 Сельдерей selderey
+521 2 Укроп ukrop
+522 2 Шпинат shpinat
+523 2 Щавель schavel
+524 2 Эндивий endiviy
+525 2 Валерианелла valerianella
+526 2 Водяной кресс vodyanoy-kress
+527 2 Индау (Руккола) indau-rukkola
+528 2 Кресс-салат kress-salat
+529 2 Портулак овощной portulak-ovoschnoy
+530 2 Рукола rukola
+531 2 Солянка solyanka
+532 2 Овощные ovoschnye
+533 2 Арбуз arbuz
+534 2 Баклажан baklazhan
+535 2 Морковь morkov
+536 2 Огурец ogurec
+537 2 Перец perec
+538 2 Редис redis
+539 2 Репа repa
+540 2 Артишок artishok
+541 2 Брюква bryukva
+542 2 Дыня dynya
+543 2 Земляника zemlyanika
+544 2 Кабачок kabachok
+545 2 Капуста kapusta
+546 2 Клубника klubnika
+547 2 Момордика momordika
+548 2 Патиссон patisson
+549 2 Редька redka
+550 2 Свекла svekla
+551 2 Скорцонера skorconera
+552 2 Спаржа sparzha
+553 2 Томат tomat
+554 2 Тыква tykva
+555 2 Физалис fizalis
+556 2 Цикорий cikoriy
+557 2 Бамия bamiya
+558 2 Лагенария lagenariya
+559 2 Люффа lyuffa
+560 2 Фенхель fenhel
+561 2 Арахис arahis
+562 2 Боб bob
+563 2 Горох goroh
+564 2 Кукуруза kukuruza
+565 2 Фасоль fasol
+566 2 Чеснок chesnok
+567 2 Минеральные удобрения mineralnye-udobreniya
+568 2 Лекарственные lekarstvennye
+569 2 Алтей altey
+570 2 Валериана valeriana
+571 2 Зверобой zveroboy
+572 2 Любисток lyubistok
+573 2 Расторопша rastoropsha
+574 2 Лаванда lavanda
+575 2 Кокос kokos
+576 2 Ежовик ezhovik
+577 2 Дренаж drenazh
+578 2 Для компоста dlya-komposta
+579 2 Для выгребных ям и септиков dlya-vygrebnyh-yam-i-septikov
+580 2 Для водоемов dlya-vodoemov
+581 2 Для борьбы с сорняками dlya-borby-s-sornyakami
+582 2 Для борьбы с грызунами dlya-borby-s-gryzunami
+583 2 Для борьбы с болезнями и вредителями dlya-borby-s-boleznyami-i-vreditelyami
+584 2 Для борьбы с болезнями dlya-borby-s-boleznyami
+585 2 Декоративные, Комнатные dekorativnye-komnatnye
+586 2 Аспарагус asparagus
+587 2 Банан banan
+588 2 Вашингтония vashingtoniya
+589 2 Драцена dracena
+590 2 Колеус koleus
+591 2 Кофейное дерево kofeynoe-derevo
+592 2 Нолина nolina
+593 2 Пальма palma
+594 2 Паслен paslen
+595 2 Перец декоративный perec-dekorativnyy
+596 2 Шеффлера shefflera
+597 2 Юкка yukka
+598 2 Декоративные dekorativnye
+599 2 Земляничный шпинат zemlyanichnyy-shpinat
+600 2 Капуста декоративная kapusta-dekorativnaya
+601 2 Клевер klever
+602 2 Клещевина kleschevina
+603 2 Книфофия (Тритома) knifofiya-tritoma
+604 2 Кохия kohiya
+605 2 Овсяница ovsyanica
+606 2 Пампасная трава pampasnaya-trava
+607 2 Тыква декоративная tykva-dekorativnaya
+608 2 Хоста hosta
+609 2 Трава газонная trava-gazonnaya
+610 2 Все для сада vse-dlya-sada
+611 2 Воспомогательные вещества vospomogatelnye-veschestva
+612 2 Вёшенка veshenka
+613 2 Белый belyy
+614 2 Агроволокно agrovolokno
+615 2 Регулятори росту regulyatori-rostu
+22 2 Страницы stranitsi
+24 2 Новости news
+\.
+
+
+--
+-- TOC entry 2548 (class 0 OID 83774)
+-- Dependencies: 180
+-- Data for Name: termin_relation; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY termin_relation (termin_id, termin_id_related) FROM stdin;
+\.
+
+
+--
+-- TOC entry 2549 (class 0 OID 83777)
+-- Dependencies: 181
+-- Data for Name: termin_structure; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY termin_structure (termin_id, termin_pid) FROM stdin;
+20 8
+321 8
+322 321
+322 508
+323 8
+324 323
+325 20
+326 325
+327 325
+328 325
+329 325
+330 20
+331 330
+332 330
+333 330
+334 330
+335 330
+336 330
+337 330
+338 330
+339 330
+340 330
+341 330
+342 330
+343 330
+344 330
+345 330
+346 330
+347 330
+348 330
+349 330
+350 330
+351 330
+352 330
+353 330
+354 330
+355 330
+356 330
+357 330
+358 330
+359 330
+360 330
+361 330
+362 330
+363 330
+364 330
+365 330
+366 330
+367 330
+368 330
+369 330
+370 330
+371 330
+372 330
+373 330
+374 330
+375 330
+376 330
+377 330
+378 330
+379 330
+380 330
+381 330
+382 330
+383 330
+384 330
+385 330
+386 330
+387 330
+388 330
+389 330
+390 330
+391 330
+392 330
+393 330
+394 330
+395 330
+396 330
+397 330
+398 330
+399 330
+400 330
+401 330
+402 330
+403 330
+404 330
+405 330
+406 330
+407 330
+408 330
+409 330
+410 330
+411 330
+412 330
+413 330
+414 330
+415 330
+416 330
+417 330
+418 330
+419 330
+420 330
+421 330
+422 330
+423 330
+424 330
+425 330
+426 330
+427 330
+428 330
+429 330
+430 330
+431 330
+432 330
+433 330
+434 330
+434 568
+435 330
+436 330
+437 330
+438 330
+439 330
+440 330
+441 330
+442 330
+443 330
+444 330
+445 330
+446 330
+447 330
+448 330
+449 330
+450 330
+451 330
+452 330
+453 330
+454 330
+455 330
+456 330
+457 330
+458 330
+459 330
+460 330
+461 330
+462 330
+463 330
+464 330
+465 330
+466 330
+467 8
+468 467
+469 467
+470 8
+471 470
+472 470
+473 323
+474 8
+475 474
+476 470
+477 474
+478 20
+479 478
+480 478
+481 478
+482 478
+483 478
+484 478
+485 478
+486 478
+487 478
+488 20
+489 488
+490 488
+491 488
+492 20
+493 492
+494 492
+495 492
+496 492
+497 492
+498 492
+499 492
+500 492
+500 502
+501 492
+502 20
+503 502
+504 502
+504 514
+505 502
+506 502
+507 321
+507 508
+508 8
+509 467
+510 323
+511 20
+512 511
+513 511
+513 532
+514 20
+515 514
+515 532
+516 514
+517 514
+518 514
+519 514
+520 514
+520 532
+521 514
+2 22
+3 22
+4 22
+5 22
+6 22
+7 22
+9 22
+10 22
+11 22
+12 22
+13 22
+14 22
+15 22
+16 22
+522 514
+523 514
+524 514
+525 514
+526 514
+527 514
+528 514
+529 514
+530 514
+531 514
+532 20
+533 532
+534 532
+535 532
+536 532
+537 532
+538 532
+539 532
+540 532
+541 532
+542 532
+543 532
+544 532
+545 532
+546 532
+547 532
+548 532
+549 532
+550 532
+551 532
+552 532
+553 532
+554 532
+555 532
+556 532
+557 532
+558 532
+559 532
+560 532
+561 532
+562 532
+563 532
+564 532
+565 532
+566 532
+567 467
+568 20
+569 568
+570 568
+571 568
+572 568
+573 568
+574 568
+575 474
+576 323
+577 474
+578 508
+579 508
+580 508
+581 321
+582 470
+583 508
+584 321
+584 508
+585 20
+586 585
+587 585
+588 585
+589 585
+590 585
+591 585
+592 585
+593 585
+594 585
+595 585
+596 585
+597 585
+598 20
+599 598
+600 598
+601 598
+602 598
+603 598
+604 598
+605 598
+606 598
+607 598
+608 598
+609 598
+610 470
+611 321
+612 323
+613 323
+614 470
+615 8
+1 22
+8 0
+22 0
+24 0
+\.
+
+
+--
+-- TOC entry 2634 (class 0 OID 0)
+-- Dependencies: 182
+-- Name: termin_termin_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('termin_termin_id_seq', 24, true);
+
+
+--
+-- TOC entry 2551 (class 0 OID 83782)
+-- Dependencies: 183
+-- Data for Name: user; Type: TABLE DATA; Schema: public; Owner: postgres
+--
+
+COPY "user" (id, username, lastname, firstname, middlename, auth_key, password_hash, password_reset_token, email, status, created_at, updated_at) FROM stdin;
+1 kingofdusk \N \N \N Up7QfH1esuEcd95Vuu065B_CCMnOsHO5 $2y$13$h.ZswcXs4Th836uRgdHGJOQqFrTiBlbyP42LwnL4nnv6GLJcAn7OG \N kingofdusk@gmail.com 10 1449053904 1449053904
+3 admin \N \N \N SlJ315le5enaDJ4OsdvXX83i_drf-JXl $2y$13$LfYpoaZkKzniNw/7rySAnOVPHy8wKFFznxBVoAWTnbLv/Di0oXqvi \N admin@admin.com 10 1449217990 1449217990
+\.
+
+
+--
+-- TOC entry 2635 (class 0 OID 0)
+-- Dependencies: 184
+-- Name: user_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
+--
+
+SELECT pg_catalog.setval('user_id_seq', 4, true);
+
+
+--
+-- TOC entry 2332 (class 2606 OID 83976)
+-- Name: admin_menu_access_group_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY admin_menu_access_group
+ ADD CONSTRAINT admin_menu_access_group_pkey PRIMARY KEY (admin_menu_access_group_id);
+
+
+--
+-- TOC entry 2336 (class 2606 OID 83978)
+-- Name: admin_menu_access_user_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY admin_menu_access_user
+ ADD CONSTRAINT admin_menu_access_user_pkey PRIMARY KEY (admin_menu_access_user_id);
+
+
+--
+-- TOC entry 2326 (class 2606 OID 83980)
+-- Name: admin_menu_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY admin_menu
+ ADD CONSTRAINT admin_menu_pkey PRIMARY KEY (admin_menu_id);
+
+
+--
+-- TOC entry 2362 (class 2606 OID 84169)
+-- Name: article_category_lang_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY article_category_lang
+ ADD CONSTRAINT article_category_lang_pkey PRIMARY KEY (article_category_lang_id);
+
+
+--
+-- TOC entry 2368 (class 2606 OID 84181)
+-- Name: article_category_media_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY article_category_media
+ ADD CONSTRAINT article_category_media_pkey PRIMARY KEY (article_category_media_id);
+
+
+--
+-- TOC entry 2357 (class 2606 OID 84141)
+-- Name: article_category_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY article_category
+ ADD CONSTRAINT article_category_pkey PRIMARY KEY (article_category_id);
+
+
+--
+-- TOC entry 2373 (class 2606 OID 84201)
+-- Name: article_lang_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY article_lang
+ ADD CONSTRAINT article_lang_pkey PRIMARY KEY (article_lang_id);
+
+
+--
+-- TOC entry 2379 (class 2606 OID 84213)
+-- Name: article_media_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY article_media
+ ADD CONSTRAINT article_media_pkey PRIMARY KEY (article_media_id);
+
+
+--
+-- TOC entry 2352 (class 2606 OID 84127)
+-- Name: article_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY article
+ ADD CONSTRAINT article_pkey PRIMARY KEY (article_id);
+
+
+--
+-- TOC entry 2339 (class 2606 OID 83982)
+-- Name: auth_assignment_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY auth_assignment
+ ADD CONSTRAINT auth_assignment_pkey PRIMARY KEY (item_name, user_id);
+
+
+--
+-- TOC entry 2344 (class 2606 OID 83984)
+-- Name: auth_item_child_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY auth_item_child
+ ADD CONSTRAINT auth_item_child_pkey PRIMARY KEY (parent, child);
+
+
+--
+-- TOC entry 2341 (class 2606 OID 83986)
+-- Name: auth_item_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY auth_item
+ ADD CONSTRAINT auth_item_pkey PRIMARY KEY (name);
+
+
+--
+-- TOC entry 2346 (class 2606 OID 83988)
+-- Name: auth_rule_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY auth_rule
+ ADD CONSTRAINT auth_rule_pkey PRIMARY KEY (name);
+
+
+--
+-- TOC entry 2314 (class 2606 OID 83895)
+-- Name: catalog_i18n_alias_langid_unique; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY catalog_i18n
+ ADD CONSTRAINT catalog_i18n_alias_langid_unique UNIQUE (alias, lang_id);
+
+
+--
+-- TOC entry 2316 (class 2606 OID 83897)
+-- Name: catalog_i18n_catalog_langid_pk; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY catalog_i18n
+ ADD CONSTRAINT catalog_i18n_catalog_langid_pk PRIMARY KEY (catalog_id, lang_id);
+
+
+--
+-- TOC entry 2312 (class 2606 OID 83899)
+-- Name: catalog_id_pk; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY catalog
+ ADD CONSTRAINT catalog_id_pk PRIMARY KEY (catalog_id);
+
+
+--
+-- TOC entry 2279 (class 2606 OID 83794)
+-- Name: language_id; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY language
+ ADD CONSTRAINT language_id PRIMARY KEY (language_id);
+
+
+--
+-- TOC entry 2282 (class 2606 OID 83796)
+-- Name: language_lang_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY language_lang
+ ADD CONSTRAINT language_lang_pkey PRIMARY KEY (language_id, lang_id);
+
+
+--
+-- TOC entry 2382 (class 2606 OID 84188)
+-- Name: media_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY media
+ ADD CONSTRAINT media_pkey PRIMARY KEY (media_id);
+
+
+--
+-- TOC entry 2292 (class 2606 OID 83798)
+-- Name: menu_location_lang_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY menu_location_lang
+ ADD CONSTRAINT menu_location_lang_pkey PRIMARY KEY (menu_location_id, lang_id);
+
+
+--
+-- TOC entry 2289 (class 2606 OID 83800)
+-- Name: menu_location_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY menu_location
+ ADD CONSTRAINT menu_location_pkey PRIMARY KEY (menu_location_id);
+
+
+--
+-- TOC entry 2286 (class 2606 OID 83802)
+-- Name: menu_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY menu
+ ADD CONSTRAINT menu_pkey PRIMARY KEY (menu_id);
+
+
+--
+-- TOC entry 2392 (class 2606 OID 84248)
+-- Name: option_lang_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY option_lang
+ ADD CONSTRAINT option_lang_pkey PRIMARY KEY (option_lang_id);
+
+
+--
+-- TOC entry 2387 (class 2606 OID 84241)
+-- Name: option_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY option
+ ADD CONSTRAINT option_pkey PRIMARY KEY (option_id);
+
+
+--
+-- TOC entry 2348 (class 2606 OID 83990)
+-- Name: social_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY social
+ ADD CONSTRAINT social_pkey PRIMARY KEY (social_id);
+
+
+--
+-- TOC entry 2321 (class 2606 OID 83901)
+-- Name: termin_book_lang_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY termin_book_lang
+ ADD CONSTRAINT termin_book_lang_pkey PRIMARY KEY (termin_book_id, lang_id);
+
+
+--
+-- TOC entry 2318 (class 2606 OID 83903)
+-- Name: termin_book_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY termin_book
+ ADD CONSTRAINT termin_book_pkey PRIMARY KEY (termin_book_id);
+
+
+--
+-- TOC entry 2298 (class 2606 OID 83804)
+-- Name: termin_lang_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY termin_lang
+ ADD CONSTRAINT termin_lang_pkey PRIMARY KEY (termin_id, language_id);
+
+
+--
+-- TOC entry 2295 (class 2606 OID 83806)
+-- Name: termin_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY termin
+ ADD CONSTRAINT termin_pkey PRIMARY KEY (termin_id);
+
+
+--
+-- TOC entry 2301 (class 2606 OID 83808)
+-- Name: termin_structure_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY termin_structure
+ ADD CONSTRAINT termin_structure_pkey PRIMARY KEY (termin_id, termin_pid);
+
+
+--
+-- TOC entry 2304 (class 2606 OID 83810)
+-- Name: user_email_key; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY "user"
+ ADD CONSTRAINT user_email_key UNIQUE (email);
+
+
+--
+-- TOC entry 2306 (class 2606 OID 83812)
+-- Name: user_password_reset_token_key; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY "user"
+ ADD CONSTRAINT user_password_reset_token_key UNIQUE (password_reset_token);
+
+
+--
+-- TOC entry 2308 (class 2606 OID 83814)
+-- Name: user_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY "user"
+ ADD CONSTRAINT user_pkey PRIMARY KEY (id);
+
+
+--
+-- TOC entry 2310 (class 2606 OID 83816)
+-- Name: user_username_key; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace:
+--
+
+ALTER TABLE ONLY "user"
+ ADD CONSTRAINT user_username_key UNIQUE (username);
+
+
+--
+-- TOC entry 2327 (class 1259 OID 83991)
+-- Name: 1; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX "1" ON admin_menu_access_group USING btree ("group");
+
+
+--
+-- TOC entry 2328 (class 1259 OID 83992)
+-- Name: admin_menu_access_group_group_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX admin_menu_access_group_group_idx ON admin_menu_access_group USING btree ("group");
+
+
+--
+-- TOC entry 2329 (class 1259 OID 83993)
+-- Name: admin_menu_access_group_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX admin_menu_access_group_id_idx ON admin_menu_access_group USING btree (admin_menu_access_group_id);
+
+
+--
+-- TOC entry 2330 (class 1259 OID 83994)
+-- Name: admin_menu_access_group_menu_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX admin_menu_access_group_menu_id_idx ON admin_menu_access_group USING btree (admin_menu_id);
+
+
+--
+-- TOC entry 2333 (class 1259 OID 83995)
+-- Name: admin_menu_access_user_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX admin_menu_access_user_id_idx ON admin_menu_access_user USING btree (admin_menu_access_user_id);
+
+
+--
+-- TOC entry 2334 (class 1259 OID 83996)
+-- Name: admin_menu_access_user_menu_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX admin_menu_access_user_menu_id_idx ON admin_menu_access_user USING btree (admin_menu_id);
+
+
+--
+-- TOC entry 2337 (class 1259 OID 83997)
+-- Name: admin_menu_access_user_user_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX admin_menu_access_user_user_id_idx ON admin_menu_access_user USING btree (user_id);
+
+
+--
+-- TOC entry 2323 (class 1259 OID 83998)
+-- Name: admin_menu_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX admin_menu_id_idx ON admin_menu USING btree (admin_menu_id);
+
+
+--
+-- TOC entry 2324 (class 1259 OID 83999)
+-- Name: admin_menu_parent_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX admin_menu_parent_id_idx ON admin_menu USING btree (admin_menu_pid);
+
+
+--
+-- TOC entry 2349 (class 1259 OID 84123)
+-- Name: article_article_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_article_id_idx ON article USING btree (article_id);
+
+
+--
+-- TOC entry 2350 (class 1259 OID 84125)
+-- Name: article_article_pid_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_article_pid_idx ON article USING btree (article_pid);
+
+
+--
+-- TOC entry 2354 (class 1259 OID 84138)
+-- Name: article_category_article_category_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_article_category_id_idx ON article_category USING btree (article_category_id);
+
+
+--
+-- TOC entry 2355 (class 1259 OID 84139)
+-- Name: article_category_article_category_pid_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_article_category_pid_idx ON article_category USING btree (article_category_pid);
+
+
+--
+-- TOC entry 2358 (class 1259 OID 84149)
+-- Name: article_category_lang_article_category_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_lang_article_category_id_idx ON article_category_lang USING btree (article_category_id);
+
+
+--
+-- TOC entry 2359 (class 1259 OID 84147)
+-- Name: article_category_lang_article_category_lang_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_lang_article_category_lang_id_idx ON article_category_lang USING btree (article_category_lang_id);
+
+
+--
+-- TOC entry 2360 (class 1259 OID 84148)
+-- Name: article_category_lang_language_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_lang_language_id_idx ON article_category_lang USING btree (language_id);
+
+
+--
+-- TOC entry 2363 (class 1259 OID 84151)
+-- Name: article_category_media_article_category_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_media_article_category_id_idx ON article_category_media USING btree (article_category_id);
+
+
+--
+-- TOC entry 2364 (class 1259 OID 84150)
+-- Name: article_category_media_article_category_media_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_media_article_category_media_id_idx ON article_category_media USING btree (article_category_media_id);
+
+
+--
+-- TOC entry 2365 (class 1259 OID 84194)
+-- Name: article_category_media_language_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_media_language_id_idx ON article_category_media USING btree (language_id);
+
+
+--
+-- TOC entry 2366 (class 1259 OID 84152)
+-- Name: article_category_media_media_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_category_media_media_id_idx ON article_category_media USING btree (media_id);
+
+
+--
+-- TOC entry 2369 (class 1259 OID 84155)
+-- Name: article_lang_article_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_lang_article_id_idx ON article_lang USING btree (article_id);
+
+
+--
+-- TOC entry 2370 (class 1259 OID 84153)
+-- Name: article_lang_article_lang_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_lang_article_lang_id_idx ON article_lang USING btree (article_lang_id);
+
+
+--
+-- TOC entry 2371 (class 1259 OID 84154)
+-- Name: article_lang_language_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_lang_language_id_idx ON article_lang USING btree (language_id);
+
+
+--
+-- TOC entry 2374 (class 1259 OID 84157)
+-- Name: article_media_article_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_media_article_id_idx ON article_media USING btree (article_id);
+
+
+--
+-- TOC entry 2375 (class 1259 OID 84156)
+-- Name: article_media_article_media_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_media_article_media_id_idx ON article_media USING btree (article_media_id);
+
+
+--
+-- TOC entry 2376 (class 1259 OID 84214)
+-- Name: article_media_language_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_media_language_id_idx ON article_media USING btree (language_id);
+
+
+--
+-- TOC entry 2377 (class 1259 OID 84158)
+-- Name: article_media_media_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_media_media_id_idx ON article_media USING btree (media_id);
+
+
+--
+-- TOC entry 2393 (class 1259 OID 84160)
+-- Name: article_to_category_article_category_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_to_category_article_category_id_idx ON article_to_category USING btree (article_category_id);
+
+
+--
+-- TOC entry 2394 (class 1259 OID 84159)
+-- Name: article_to_category_article_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_to_category_article_id_idx ON article_to_category USING btree (article_id);
+
+
+--
+-- TOC entry 2353 (class 1259 OID 84124)
+-- Name: article_user_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX article_user_id_idx ON article USING btree (user_id);
+
+
+--
+-- TOC entry 2342 (class 1259 OID 84000)
+-- Name: auth_item_type_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX auth_item_type_idx ON auth_item USING btree (type);
+
+
+--
+-- TOC entry 2280 (class 1259 OID 83817)
+-- Name: language_lang_lang_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX language_lang_lang_id_idx ON language_lang USING btree (lang_id);
+
+
+--
+-- TOC entry 2380 (class 1259 OID 84161)
+-- Name: media_media_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX media_media_id_idx ON media USING btree (media_id);
+
+
+--
+-- TOC entry 2290 (class 1259 OID 83818)
+-- Name: menu_location_lang_lang_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX menu_location_lang_lang_id_idx ON menu_location_lang USING btree (lang_id);
+
+
+--
+-- TOC entry 2283 (class 1259 OID 83819)
+-- Name: menu_menu_location_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX menu_menu_location_id_idx ON menu USING btree (menu_location_id);
+
+
+--
+-- TOC entry 2284 (class 1259 OID 83820)
+-- Name: menu_menu_pid_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX menu_menu_pid_idx ON menu USING btree (menu_pid);
+
+
+--
+-- TOC entry 2287 (class 1259 OID 83821)
+-- Name: menu_termin_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX menu_termin_id_idx ON menu USING btree (termin_id);
+
+
+--
+-- TOC entry 2388 (class 1259 OID 84167)
+-- Name: option_lang_language_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX option_lang_language_id_idx ON option_lang USING btree (language_id);
+
+
+--
+-- TOC entry 2389 (class 1259 OID 84166)
+-- Name: option_lang_option_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX option_lang_option_id_idx ON option_lang USING btree (option_id);
+
+
+--
+-- TOC entry 2390 (class 1259 OID 84165)
+-- Name: option_lang_option_lang_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX option_lang_option_lang_id_idx ON option_lang USING btree (option_lang_id);
+
+
+--
+-- TOC entry 2383 (class 1259 OID 84164)
+-- Name: option_model_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX option_model_id_idx ON option USING btree (model_id);
+
+
+--
+-- TOC entry 2384 (class 1259 OID 84162)
+-- Name: option_option_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX option_option_id_idx ON option USING btree (option_id);
+
+
+--
+-- TOC entry 2385 (class 1259 OID 84163)
+-- Name: option_option_pid_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX option_option_pid_idx ON option USING btree (option_pid);
+
+
+--
+-- TOC entry 2319 (class 1259 OID 83904)
+-- Name: termin_book_lang_lang_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX termin_book_lang_lang_id_idx ON termin_book_lang USING btree (lang_id);
+
+
+--
+-- TOC entry 2322 (class 1259 OID 83905)
+-- Name: termin_book_lang_termin_book_id_lang_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX termin_book_lang_termin_book_id_lang_id_idx ON termin_book_lang USING btree (termin_book_id, lang_id);
+
+
+--
+-- TOC entry 2293 (class 1259 OID 83822)
+-- Name: termin_is_book_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX termin_is_book_idx ON termin USING btree (is_book);
+
+
+--
+-- TOC entry 2296 (class 1259 OID 83823)
+-- Name: termin_lang_lang_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX termin_lang_lang_id_idx ON termin_lang USING btree (language_id);
+
+
+--
+-- TOC entry 2299 (class 1259 OID 83824)
+-- Name: termin_lang_termin_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX termin_lang_termin_id_idx ON termin_lang USING btree (termin_id);
+
+
+--
+-- TOC entry 2302 (class 1259 OID 83825)
+-- Name: termin_structure_termin_pid_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
+--
+
+CREATE INDEX termin_structure_termin_pid_idx ON termin_structure USING btree (termin_pid);
+
+
+--
+-- TOC entry 2406 (class 2606 OID 84001)
+-- Name: admin_menu_access_group_menu_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY admin_menu_access_group
+ ADD CONSTRAINT admin_menu_access_group_menu_id_fkey FOREIGN KEY (admin_menu_id) REFERENCES admin_menu(admin_menu_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2407 (class 2606 OID 84006)
+-- Name: admin_menu_access_user_menu_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY admin_menu_access_user
+ ADD CONSTRAINT admin_menu_access_user_menu_id_fkey FOREIGN KEY (admin_menu_id) REFERENCES admin_menu(admin_menu_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2408 (class 2606 OID 84011)
+-- Name: admin_menu_access_user_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY admin_menu_access_user
+ ADD CONSTRAINT admin_menu_access_user_user_id_fkey FOREIGN KEY (user_id) REFERENCES "user"(id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2405 (class 2606 OID 84016)
+-- Name: admin_menu_parent_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY admin_menu
+ ADD CONSTRAINT admin_menu_parent_id_fkey FOREIGN KEY (admin_menu_pid) REFERENCES admin_menu(admin_menu_id) ON UPDATE CASCADE ON DELETE SET NULL;
+
+
+--
+-- TOC entry 2413 (class 2606 OID 84128)
+-- Name: article_article_pid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article
+ ADD CONSTRAINT article_article_pid_fkey FOREIGN KEY (article_pid) REFERENCES article(article_id) ON UPDATE CASCADE ON DELETE SET NULL;
+
+
+--
+-- TOC entry 2415 (class 2606 OID 84142)
+-- Name: article_category_article_category_pid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category
+ ADD CONSTRAINT article_category_article_category_pid_fkey FOREIGN KEY (article_category_pid) REFERENCES article_category(article_category_id) ON UPDATE CASCADE ON DELETE SET NULL;
+
+
+--
+-- TOC entry 2417 (class 2606 OID 84175)
+-- Name: article_category_lang_article_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category_lang
+ ADD CONSTRAINT article_category_lang_article_category_id_fkey FOREIGN KEY (article_category_id) REFERENCES article_category(article_category_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2416 (class 2606 OID 84170)
+-- Name: article_category_lang_language_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category_lang
+ ADD CONSTRAINT article_category_lang_language_id_fkey FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE SET DEFAULT;
+
+
+--
+-- TOC entry 2418 (class 2606 OID 84182)
+-- Name: article_category_media_article_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category_media
+ ADD CONSTRAINT article_category_media_article_category_id_fkey FOREIGN KEY (article_category_id) REFERENCES article_category(article_category_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2420 (class 2606 OID 84195)
+-- Name: article_category_media_language_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category_media
+ ADD CONSTRAINT article_category_media_language_id_fkey FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE SET DEFAULT;
+
+
+--
+-- TOC entry 2419 (class 2606 OID 84189)
+-- Name: article_category_media_media_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_category_media
+ ADD CONSTRAINT article_category_media_media_id_fkey FOREIGN KEY (media_id) REFERENCES media(media_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2422 (class 2606 OID 84207)
+-- Name: article_lang_article_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_lang
+ ADD CONSTRAINT article_lang_article_id_fkey FOREIGN KEY (article_id) REFERENCES article(article_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2421 (class 2606 OID 84202)
+-- Name: article_lang_language_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_lang
+ ADD CONSTRAINT article_lang_language_id_fkey FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE SET DEFAULT;
+
+
+--
+-- TOC entry 2423 (class 2606 OID 84215)
+-- Name: article_media_article_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_media
+ ADD CONSTRAINT article_media_article_id_fkey FOREIGN KEY (article_id) REFERENCES article(article_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2425 (class 2606 OID 84225)
+-- Name: article_media_language_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_media
+ ADD CONSTRAINT article_media_language_id_fkey FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE SET DEFAULT;
+
+
+--
+-- TOC entry 2424 (class 2606 OID 84220)
+-- Name: article_media_media_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_media
+ ADD CONSTRAINT article_media_media_id_fkey FOREIGN KEY (media_id) REFERENCES media(media_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2430 (class 2606 OID 84235)
+-- Name: article_to_category_article_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_to_category
+ ADD CONSTRAINT article_to_category_article_category_id_fkey FOREIGN KEY (article_category_id) REFERENCES article_category(article_category_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2429 (class 2606 OID 84230)
+-- Name: article_to_category_article_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article_to_category
+ ADD CONSTRAINT article_to_category_article_id_fkey FOREIGN KEY (article_id) REFERENCES article(article_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2414 (class 2606 OID 84133)
+-- Name: article_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY article
+ ADD CONSTRAINT article_user_id_fkey FOREIGN KEY (user_id) REFERENCES "user"(id) ON UPDATE CASCADE ON DELETE SET NULL;
+
+
+--
+-- TOC entry 2409 (class 2606 OID 84021)
+-- Name: auth_assignment_item_name_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY auth_assignment
+ ADD CONSTRAINT auth_assignment_item_name_fkey FOREIGN KEY (item_name) REFERENCES auth_item(name) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2411 (class 2606 OID 84026)
+-- Name: auth_item_child_child_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY auth_item_child
+ ADD CONSTRAINT auth_item_child_child_fkey FOREIGN KEY (child) REFERENCES auth_item(name) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2412 (class 2606 OID 84031)
+-- Name: auth_item_child_parent_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY auth_item_child
+ ADD CONSTRAINT auth_item_child_parent_fkey FOREIGN KEY (parent) REFERENCES auth_item(name) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2410 (class 2606 OID 84036)
+-- Name: auth_item_rule_name_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY auth_item
+ ADD CONSTRAINT auth_item_rule_name_fkey FOREIGN KEY (rule_name) REFERENCES auth_rule(name) ON UPDATE CASCADE ON DELETE SET NULL;
+
+
+--
+-- TOC entry 2395 (class 2606 OID 83826)
+-- Name: language_lang_language_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY language_lang
+ ADD CONSTRAINT language_lang_language_id_fkey FOREIGN KEY (language_id) REFERENCES language(language_id);
+
+
+--
+-- TOC entry 2396 (class 2606 OID 83831)
+-- Name: language_lang_language_id_fkey1; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY language_lang
+ ADD CONSTRAINT language_lang_language_id_fkey1 FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2398 (class 2606 OID 83836)
+-- Name: menu_location_lang_lang_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY menu_location_lang
+ ADD CONSTRAINT menu_location_lang_lang_id_fkey FOREIGN KEY (lang_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2399 (class 2606 OID 83841)
+-- Name: menu_location_lang_menu_location_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY menu_location_lang
+ ADD CONSTRAINT menu_location_lang_menu_location_id_fkey FOREIGN KEY (menu_location_id) REFERENCES menu_location(menu_location_id);
+
+
+--
+-- TOC entry 2397 (class 2606 OID 83846)
+-- Name: menu_termin_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY menu
+ ADD CONSTRAINT menu_termin_id_fkey FOREIGN KEY (termin_id) REFERENCES termin(termin_id);
+
+
+--
+-- TOC entry 2428 (class 2606 OID 84254)
+-- Name: option_lang_language_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY option_lang
+ ADD CONSTRAINT option_lang_language_id_fkey FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE SET DEFAULT;
+
+
+--
+-- TOC entry 2427 (class 2606 OID 84249)
+-- Name: option_lang_option_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY option_lang
+ ADD CONSTRAINT option_lang_option_id_fkey FOREIGN KEY (option_id) REFERENCES option(option_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2426 (class 2606 OID 84242)
+-- Name: option_option_pid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY option
+ ADD CONSTRAINT option_option_pid_fkey FOREIGN KEY (option_pid) REFERENCES option(option_id) ON UPDATE CASCADE ON DELETE SET NULL;
+
+
+--
+-- TOC entry 2403 (class 2606 OID 83906)
+-- Name: termin_book_lang_lang_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY termin_book_lang
+ ADD CONSTRAINT termin_book_lang_lang_id_fkey FOREIGN KEY (lang_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2404 (class 2606 OID 83911)
+-- Name: termin_book_lang_termin_book_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY termin_book_lang
+ ADD CONSTRAINT termin_book_lang_termin_book_id_fkey FOREIGN KEY (termin_book_id) REFERENCES termin_book(termin_book_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2400 (class 2606 OID 83851)
+-- Name: termin_lang_lang_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY termin_lang
+ ADD CONSTRAINT termin_lang_lang_id_fkey FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2401 (class 2606 OID 83856)
+-- Name: termin_lang_termin_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY termin_lang
+ ADD CONSTRAINT termin_lang_termin_id_fkey FOREIGN KEY (termin_id) REFERENCES termin(termin_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2402 (class 2606 OID 83861)
+-- Name: termin_structure_termin_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+--
+
+ALTER TABLE ONLY termin_structure
+ ADD CONSTRAINT termin_structure_termin_id_fkey FOREIGN KEY (termin_id) REFERENCES termin(termin_id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 2596 (class 0 OID 0)
+-- Dependencies: 6
+-- Name: public; Type: ACL; Schema: -; Owner: postgres
+--
+
+REVOKE ALL ON SCHEMA public FROM PUBLIC;
+REVOKE ALL ON SCHEMA public FROM postgres;
+GRANT ALL ON SCHEMA public TO postgres;
+GRANT ALL ON SCHEMA public TO PUBLIC;
+
+
+-- Completed on 2016-01-15 15:34:48
+
+--
+-- PostgreSQL database dump complete
+--
+
diff --git a/db-migration/yarik/admin_menu.backup b/db-migration/yarik/admin_menu.backup
new file mode 100644
index 0000000..4c6ae35
Binary files /dev/null and b/db-migration/yarik/admin_menu.backup differ
diff --git a/db-migration/yarik/all.backup b/db-migration/yarik/all.backup
new file mode 100644
index 0000000..f581fe1
Binary files /dev/null and b/db-migration/yarik/all.backup differ
diff --git a/db-migration/yarik/article.backup b/db-migration/yarik/article.backup
new file mode 100644
index 0000000..d053b30
Binary files /dev/null and b/db-migration/yarik/article.backup differ
diff --git a/db-migration/yarik/language.backup b/db-migration/yarik/language.backup
new file mode 100644
index 0000000..58a3a96
Binary files /dev/null and b/db-migration/yarik/language.backup differ
diff --git a/db-migration/yarik/media.backup b/db-migration/yarik/media.backup
new file mode 100644
index 0000000..a3db235
Binary files /dev/null and b/db-migration/yarik/media.backup differ
diff --git a/db-migration/yarik/option.backup b/db-migration/yarik/option.backup
new file mode 100644
index 0000000..fa0d022
Binary files /dev/null and b/db-migration/yarik/option.backup differ
diff --git a/environments/dev/backend/config/main-local.php b/environments/dev/backend/config/main-local.php
new file mode 100644
index 0000000..d9a8ceb
--- /dev/null
+++ b/environments/dev/backend/config/main-local.php
@@ -0,0 +1,25 @@
+ [
+ 'request' => [
+ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
+ 'cookieValidationKey' => '',
+ ],
+ ],
+];
+
+if (!YII_ENV_TEST) {
+ // configuration adjustments for 'dev' environment
+ $config['bootstrap'][] = 'debug';
+ $config['modules']['debug'] = [
+ 'class' => 'yii\debug\Module',
+ ];
+
+ $config['bootstrap'][] = 'gii';
+ $config['modules']['gii'] = [
+ 'class' => 'yii\gii\Module',
+ ];
+}
+
+return $config;
diff --git a/environments/dev/backend/config/params-local.php b/environments/dev/backend/config/params-local.php
new file mode 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/environments/dev/backend/config/params-local.php
@@ -0,0 +1,3 @@
+run();
diff --git a/environments/dev/backend/web/index.php b/environments/dev/backend/web/index.php
new file mode 100644
index 0000000..6038167
--- /dev/null
+++ b/environments/dev/backend/web/index.php
@@ -0,0 +1,18 @@
+run();
diff --git a/environments/dev/common/config/main-local.php b/environments/dev/common/config/main-local.php
new file mode 100644
index 0000000..43db30e
--- /dev/null
+++ b/environments/dev/common/config/main-local.php
@@ -0,0 +1,20 @@
+ [
+ 'db' => [
+ 'class' => 'yii\db\Connection',
+ 'dsn' => 'mysql:host=localhost;dbname=yii2advanced',
+ 'username' => 'root',
+ 'password' => '',
+ 'charset' => 'utf8',
+ ],
+ 'mailer' => [
+ 'class' => 'yii\swiftmailer\Mailer',
+ 'viewPath' => '@common/mail',
+ // send all mails to a file by default. You have to set
+ // 'useFileTransport' to false and configure a transport
+ // for the mailer to send real emails.
+ 'useFileTransport' => true,
+ ],
+ ],
+];
diff --git a/environments/dev/common/config/params-local.php b/environments/dev/common/config/params-local.php
new file mode 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/environments/dev/common/config/params-local.php
@@ -0,0 +1,3 @@
+ ['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 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/environments/dev/console/config/params-local.php
@@ -0,0 +1,3 @@
+ [
+ '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'] = 'yii\debug\Module';
+
+ $config['bootstrap'][] = 'gii';
+ $config['modules']['gii'] = 'yii\gii\Module';
+}
+
+return $config;
diff --git a/environments/dev/frontend/config/params-local.php b/environments/dev/frontend/config/params-local.php
new file mode 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/environments/dev/frontend/config/params-local.php
@@ -0,0 +1,3 @@
+run();
diff --git a/environments/dev/frontend/web/index.php b/environments/dev/frontend/web/index.php
new file mode 100644
index 0000000..6038167
--- /dev/null
+++ b/environments/dev/frontend/web/index.php
@@ -0,0 +1,18 @@
+run();
diff --git a/environments/dev/yii b/environments/dev/yii
new file mode 100644
index 0000000..6f0c6d2
--- /dev/null
+++ b/environments/dev/yii
@@ -0,0 +1,28 @@
+#!/usr/bin/env php
+run();
+exit($exitCode);
diff --git a/environments/index.php b/environments/index.php
new file mode 100644
index 0000000..19c989d
--- /dev/null
+++ b/environments/index.php
@@ -0,0 +1,65 @@
+ [
+ * 'path' => 'directory storing the local files',
+ * 'skipFiles' => [
+ * // list of files that should only copied once and skipped if they already exist
+ * ],
+ * 'setWritable' => [
+ * // list of directories that should be set writable
+ * ],
+ * 'setExecutable' => [
+ * // list of files that should be set executable
+ * ],
+ * 'setCookieValidationKey' => [
+ * // list of config files that need to be inserted with automatically generated cookie validation keys
+ * ],
+ * 'createSymlink' => [
+ * // list of symlinks to be created. Keys are symlinks, and values are the targets.
+ * ],
+ * ],
+ * ];
+ * ```
+ */
+return [
+ 'Development' => [
+ 'path' => 'dev',
+ 'setWritable' => [
+ 'backend/runtime',
+ 'backend/web/assets',
+ 'frontend/runtime',
+ 'frontend/web/assets',
+ ],
+ 'setExecutable' => [
+ 'yii',
+ 'tests/codeception/bin/yii',
+ ],
+ 'setCookieValidationKey' => [
+ 'backend/config/main-local.php',
+ 'frontend/config/main-local.php',
+ ],
+ ],
+ 'Production' => [
+ 'path' => 'prod',
+ 'setWritable' => [
+ 'backend/runtime',
+ 'backend/web/assets',
+ '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 100644
index 0000000..af46ba3
--- /dev/null
+++ b/environments/prod/backend/config/main-local.php
@@ -0,0 +1,9 @@
+ [
+ '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 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/environments/prod/backend/config/params-local.php
@@ -0,0 +1,3 @@
+run();
diff --git a/environments/prod/common/config/main-local.php b/environments/prod/common/config/main-local.php
new file mode 100644
index 0000000..84c4d9f
--- /dev/null
+++ b/environments/prod/common/config/main-local.php
@@ -0,0 +1,16 @@
+ [
+ 'db' => [
+ 'class' => 'yii\db\Connection',
+ 'dsn' => 'mysql:host=localhost;dbname=yii2advanced',
+ 'username' => 'root',
+ 'password' => '',
+ 'charset' => 'utf8',
+ ],
+ 'mailer' => [
+ 'class' => 'yii\swiftmailer\Mailer',
+ 'viewPath' => '@common/mail',
+ ],
+ ],
+];
diff --git a/environments/prod/common/config/params-local.php b/environments/prod/common/config/params-local.php
new file mode 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/environments/prod/common/config/params-local.php
@@ -0,0 +1,3 @@
+ [
+ '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 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/environments/prod/frontend/config/params-local.php
@@ -0,0 +1,3 @@
+run();
diff --git a/environments/prod/yii b/environments/prod/yii
new file mode 100644
index 0000000..1fe0342
--- /dev/null
+++ b/environments/prod/yii
@@ -0,0 +1,28 @@
+#!/usr/bin/env php
+run();
+exit($exitCode);
diff --git a/frontend/assets/AppAsset.php b/frontend/assets/AppAsset.php
new file mode 100644
index 0000000..f6ac539
--- /dev/null
+++ b/frontend/assets/AppAsset.php
@@ -0,0 +1,34 @@
+
+ * @since 2.0
+ */
+class AppAsset extends AssetBundle
+{
+ public $basePath = '@webroot';
+ public $baseUrl = '@web';
+ public $css = [
+ 'css/site.css',
+ 'css/flags32.css'
+ ];
+ public $js = [
+ 'js/option.js'
+ ];
+ public $depends = [
+ 'yii\web\YiiAsset',
+ 'yii\bootstrap\BootstrapAsset',
+ ];
+ public $jsOptions = array(
+ 'position' => \yii\web\View::POS_HEAD
+ );
+}
diff --git a/frontend/config/.gitignore b/frontend/config/.gitignore
new file mode 100644
index 0000000..20da318
--- /dev/null
+++ b/frontend/config/.gitignore
@@ -0,0 +1,2 @@
+main-local.php
+params-local.php
\ No newline at end of file
diff --git a/frontend/config/bootstrap.php b/frontend/config/bootstrap.php
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/frontend/config/bootstrap.php
@@ -0,0 +1 @@
+ 'app-frontend',
+ 'basePath' => dirname(__DIR__),
+ //'bootstrap' => ['log', 'PageController'],
+ 'bootstrap' => ['log'],
+ 'controllerNamespace' => 'frontend\controllers',
+ 'modules' => [
+
+ ],
+ 'components' => [
+ //'PageController'=>[
+ // 'class' => 'frontend\controllers\PageController'
+ //],
+ 'user' => [
+ 'identityClass' => 'common\models\User',
+ 'enableAutoLogin' => true,
+ ],
+ 'log' => [
+ 'traceLevel' => YII_DEBUG ? 3 : 0,
+ 'targets' => [
+ [
+ 'class' => 'yii\log\FileTarget',
+ 'levels' => ['error', 'warning'],
+ ],
+ ],
+ ],
+ 'errorHandler' => [
+ 'errorAction' => 'site/error',
+ ],
+ 'urlManager' => [
+ 'baseUrl' => '/',
+ 'enablePrettyUrl' => true,
+ 'showScriptName' => false,
+ 'rules' => [
+ //'contacts' => 'page/page'
+ ]
+ ],
+ ],
+ 'params' => $params,
+];
\ No newline at end of file
diff --git a/frontend/config/params.php b/frontend/config/params.php
new file mode 100644
index 0000000..7f754b9
--- /dev/null
+++ b/frontend/config/params.php
@@ -0,0 +1,4 @@
+ 'admin@example.com',
+];
diff --git a/frontend/controllers/CatalogController.php b/frontend/controllers/CatalogController.php
new file mode 100644
index 0000000..e8d60d6
--- /dev/null
+++ b/frontend/controllers/CatalogController.php
@@ -0,0 +1,72 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function actions()
+ {
+ return [
+ 'error' => [
+ 'class' => 'yii\web\ErrorAction',
+ ],
+ 'captcha' => [
+ 'class' => 'yii\captcha\CaptchaAction',
+ 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
+ ],
+ ];
+ }
+
+ /**
+ * Displays homepage.
+ *
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ return $this->render('index');
+ }
+
+}
diff --git a/frontend/controllers/OptionController.php b/frontend/controllers/OptionController.php
new file mode 100644
index 0000000..d2e93d8
--- /dev/null
+++ b/frontend/controllers/OptionController.php
@@ -0,0 +1,132 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all Option models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new OptionSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single Option model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new Option model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $form[0] = Option::create(\Yii::$app->request->post(), 'User', 10, [['name' => 'phone', 'template' => 'text', 'translate' => true], ['name' => 'adres', 'template' => 'text', 'translate' => false]]);
+ if($form[0]['success'] == false) {
+ return $this->render('create', ['forms' => $form]);
+ } else {
+ return $this->redirect(['index']);
+ }
+ }
+
+ /**
+ * Updates an existing Option model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $form[0] = Option::change($id, \Yii::$app->request->post(), 'User', 10);
+ if($form[0]['success'] == false) {
+ return $this->render('update', ['forms' => $form]);
+ } else {
+ return $this->redirect(['view', 'id' => $id]);
+ }
+ }
+
+ /**
+ * Deletes an existing Option model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $model = $this->findModel($id);
+ $children = $model->hasMany(Option::className(), ['parent_id' => 'option_id'])->all();
+ $langs = array();
+ if(!empty($children)) {
+ foreach($children as $child) {
+ $langs = OptionLang::findAll(['id' => $child->option_id]);
+ foreach($langs as $lang) {
+ $lang->delete();
+ }
+ $child->delete();
+ }
+ }
+ $langs = OptionLang::findAll(['id' => $id]);
+ foreach($langs as $lang) {
+ $lang->delete();
+ }
+ $model->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the Option model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Option the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = Option::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/frontend/controllers/OptionValuesController.php b/frontend/controllers/OptionValuesController.php
new file mode 100644
index 0000000..f552de1
--- /dev/null
+++ b/frontend/controllers/OptionValuesController.php
@@ -0,0 +1,147 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all OptionValues models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new OptionValuesSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single OptionValues model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new OptionValues model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $post = \Yii::$app->request->post();
+ if(is_array($post['OptionValues']['option_value_text'])) {
+ $ok = 0;
+ $first = 0;
+ $id = 0;
+ $models = array();
+ foreach($post['OptionValues']['option_value_text'] as $lang => $value) {
+ $models[$lang] = new OptionValues();
+ $models[$lang]->load(Yii::$app->request->post());
+ $models[$lang]->option_language_id = $lang;
+ $models[$lang]->option_value_text = $value;
+ if($first && $id) {
+ $models[$lang]->option_value_parent = $id;
+ }
+ if($models[$lang]->save()) {
+ $ok = 1;
+ if(!$first) {
+ $first = 1;
+ $id = $models[$lang]->option_value_id;
+ }
+ } else {
+ unset($models[$lang]);
+ }
+ }
+ if($ok) {
+ return $this->redirect(['view', 'id' => $id]);
+ }
+ }
+ $model = new OptionValues();
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->option_value_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing OptionValues model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->option_value_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing OptionValues model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the OptionValues model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return OptionValues the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = OptionValues::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/frontend/controllers/OptionsController.php b/frontend/controllers/OptionsController.php
new file mode 100644
index 0000000..2abe3cd
--- /dev/null
+++ b/frontend/controllers/OptionsController.php
@@ -0,0 +1,121 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all Options models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new OptionsSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single Options model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new Options model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new Options();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->option_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing Options model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->option_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing Options model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the Options model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Options the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = Options::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/frontend/controllers/SiteController.php b/frontend/controllers/SiteController.php
new file mode 100644
index 0000000..5146196
--- /dev/null
+++ b/frontend/controllers/SiteController.php
@@ -0,0 +1,340 @@
+ [
+ 'class' => AccessControl::className(),
+ 'only' => ['logout', 'signup', 'index'],
+ 'rules' => [
+ [
+ 'actions' => ['signup'],
+ 'allow' => true,
+ 'roles' => ['?'],
+ ],
+ [
+ 'actions' => ['logout'],
+ 'allow' => true,
+ 'roles' => ['@'],
+ ],
+ [
+ 'actions' => ['index'],
+ 'allow' => true,
+ 'roles' => ['@'],
+ ]
+ ],
+ ],
+ 'verbs' => [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'logout' => ['post'],
+ ],
+ ],
+ 'eauth' => [
+ // required to disable csrf validation on OpenID requests
+ 'class' => \nodge\eauth\openid\ControllerBehavior::className(),
+ 'only' => ['login'],
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function actions()
+ {
+ return [
+ 'error' => [
+ 'class' => 'yii\web\ErrorAction',
+ ],
+ 'captcha' => [
+ 'class' => 'yii\captcha\CaptchaAction',
+ 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
+ ],
+ ];
+ }
+
+ /**
+ * Displays homepage.
+ *
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ return $this->render('index');
+ }
+
+ /**
+ * ===================
+ * ==== BASIC YII ====
+ * ===================
+ */
+
+ /**
+ * Logs in a user.
+ *
+ * @return mixed
+ */
+ public function actionLogin()
+ {
+
+ // creat new model table Social and new model User
+ $social = new Social();
+ $user = new User();
+
+ $serviceName = Yii::$app->getRequest()->getQueryParam('service');
+
+ if (isset($serviceName)) {
+ /** @var $eauth \nodge\eauth\ServiceBase */
+ $eauth = Yii::$app->get('eauth')->getIdentity($serviceName);
+ $eauth->setRedirectUrl(Yii::$app->getUser()->getReturnUrl());
+ $eauth->setCancelUrl(Yii::$app->getUrlManager()->createAbsoluteUrl('site/login'));
+
+ try {
+ if ($eauth->authenticate()) {
+ $identity = User::findByEAuth($eauth);
+ Yii::$app->getUser()->login($identity);
+
+ //Save date get social network in database
+ if (! $social::find()->where(['social_user_id' => $identity[profile][id], 'social_name' => $identity[profile][service]])->exists()) {
+ $name = explode(' ',$identity[profile][name]);
+ $user->firstname = $name[0];
+ $user->lastname = $name[1];
+ $user->id_system_date = date("d.m.y.H:i:s");
+ $user->save();
+ $social->social_name = $identity[profile][service];
+ $social->social_user_id = $identity[profile][id];
+ $social->user_id = $user->id;
+ $social->validate();
+ $social->errors;
+ $social->save();
+ }
+
+ // special redirect with closing popup window
+ $eauth->redirect();
+ }
+ else {
+ // close popup window and redirect to cancelUrl
+ $eauth->cancel();
+ }
+ }
+ catch (\nodge\eauth\ErrorException $e) {
+ // save error to show it later
+ Yii::$app->getSession()->setFlash('error', 'EAuthException: '.$e->getMessage());
+
+ // close popup window and redirect to cancelUrl
+// $eauth->cancel();
+ $eauth->redirect($eauth->getCancelUrl());
+ }
+ }
+
+
+
+
+ if (!\Yii::$app->user->isGuest) {
+ return $this->goHome();
+ }
+
+ $model = new LoginForm();
+ if ($model->load(Yii::$app->request->post()) && $model->login()) {
+ return $this->goBack();
+ } else {
+ 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()
+ {
+ Yii::$app->user->logout();
+ $identity = Yii::$app->getUser()->getIdentity();
+ var_dump($identity[profile]);
+ die();
+
+ $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 email.');
+ }
+
+ return $this->refresh();
+ } else {
+ return $this->render('contact', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Signs user up.
+ *
+ * @return mixed
+ */
+ public function actionSignup()
+ {
+ $model = new SignupForm();
+ if ($model->load(Yii::$app->request->post())) {
+ if ($user = $model->signup()) {
+ if (Yii::$app->getUser()->login($user)) {
+ return $this->goHome();
+ }
+ }
+ }
+
+ return $this->render('signup', [
+ 'model' => $model,
+ ]);
+ }
+
+ /**
+ * Requests password reset.
+ *
+ * @return mixed
+ */
+ public function actionRequestPasswordReset()
+ {
+ $model = new PasswordResetRequestForm();
+ if ($model->load(Yii::$app->request->post()) && $model->validate()) {
+ if ($model->sendEmail()) {
+ Yii::$app->session->setFlash('success', 'Check your email for further instructions.');
+
+ return $this->goHome();
+ } else {
+ Yii::$app->session->setFlash('error', 'Sorry, we are unable to reset password for email provided.');
+ }
+ }
+
+ 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 (InvalidParamException $e) {
+ throw new BadRequestHttpException($e->getMessage());
+ }
+
+ if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) {
+ Yii::$app->session->setFlash('success', 'New password was saved.');
+
+ return $this->goHome();
+ }
+
+ return $this->render('resetPassword', [
+ 'model' => $model,
+ ]);
+ }
+
+ public function actionOptions() {
+ $option_model = new Options();
+ $option_list = $option_model->find()->where(1)->all();
+ $option_values = array();
+ $post = \Yii::$app->request->post();
+
+ if(!empty(\Yii::$app->request->post())) {
+ $options_to_values = array();
+ $hasErrors = false;
+ foreach($post['options'] as $key => $val) {
+ $options_to_values[$key] = new OptionsToValues();
+ $options_to_values[$key]['option_id'] = $val['option_id'];
+ $options_to_values[$key]->loadDefaultValues();
+ if($options_to_values[$key]->save()) {
+ $option_values[$key] = new OptionValues();
+ $option_values[$key]['option_value_id'] = $options_to_values[$key]->getAttribute('option_value_id');
+ $option_values[$key]['option_value_text'] = $val['option_value'];
+ if($options_to_values[$key]->option->getAttribute('option_translatable') == 0 || empty($val['option_language_id'])) {
+ $option_values[$key]['option_language_id'] = 0;
+ } else {
+ $option_values[$key]['option_language_id'] = $val['option_language_id'];
+ }
+ if(!$option_values[$key]->save()) {
+ $options_to_values[$key]->delete();
+ $hasErrors = true;
+ }
+ }
+ }
+ if(hasErrors) {
+ $data['option_values'] = $option_values;
+ return $this->render('options', ['options' => $data, 'post' => $post]);
+ } else {
+ var_dump($data);
+ }
+ } else {
+ foreach($option_list as $index => $option) {
+ $option_values[$option->getAttribute('option_id')] = new OptionValues();
+ }
+ $data['option_values'] = $option_values;
+ return $this->render('options', ['options' => $data, 'post' => $post]);
+ }
+ }
+
+ public function actionFeedback() {
+ $form[0] = Option::create(\Yii::$app->request->post(), 'Feedback', 1, [['name' => 'one_name', 'template' => 'text', 'translate' => false], ['name' => 'phone', 'template' => 'text', 'translate' => false], ['name' => 'message', 'template' => 'text', 'translate' => false]], false);
+ if($form[0]['success'] == false) {
+ return $this->render('feedback', ['forms' => $form]);
+ } else {
+ return $this->render('index');
+ }
+ }
+}
diff --git a/frontend/controllers/TranslateLangController.php b/frontend/controllers/TranslateLangController.php
new file mode 100644
index 0000000..241f085
--- /dev/null
+++ b/frontend/controllers/TranslateLangController.php
@@ -0,0 +1,121 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all TranslateLang models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new TranslateLangSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single TranslateLang model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new TranslateLang model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new TranslateLang();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->translate_value_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing TranslateLang model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->translate_value_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing TranslateLang model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the TranslateLang model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return TranslateLang the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = TranslateLang::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/frontend/models/ContactForm.php b/frontend/models/ContactForm.php
new file mode 100644
index 0000000..e2b4acb
--- /dev/null
+++ b/frontend/models/ContactForm.php
@@ -0,0 +1,58 @@
+ '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 boolean whether the email was sent
+ */
+ public function sendEmail($email)
+ {
+ return Yii::$app->mailer->compose()
+ ->setTo($email)
+ ->setFrom([$this->email => $this->name])
+ ->setSubject($this->subject)
+ ->setTextBody($this->body)
+ ->send();
+ }
+}
diff --git a/frontend/models/Front.php b/frontend/models/Front.php
new file mode 100644
index 0000000..be6be82
--- /dev/null
+++ b/frontend/models/Front.php
@@ -0,0 +1,25 @@
+getMenuList($location);
+ }
+}
diff --git a/frontend/models/Language.php b/frontend/models/Language.php
new file mode 100644
index 0000000..91e6d34
--- /dev/null
+++ b/frontend/models/Language.php
@@ -0,0 +1,66 @@
+ 4]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'language_id' => Yii::t('app', 'Language ID'),
+ 'lang_code' => Yii::t('app', 'Lang Code'),
+ 'is_default' => Yii::t('app', 'Is Default'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getLanguageLangs()
+ {
+ return $this->hasMany(LanguageLang::className(), ['language_id' => 'language_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getOptionValues()
+ {
+ return $this->hasMany(OptionValues::className(), ['option_language_id' => 'language_id']);
+ }
+}
diff --git a/frontend/models/LanguageLang.php b/frontend/models/LanguageLang.php
new file mode 100644
index 0000000..78ed820
--- /dev/null
+++ b/frontend/models/LanguageLang.php
@@ -0,0 +1,57 @@
+ 256]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'language_id' => Yii::t('app', 'Language ID'),
+ 'lang_title' => Yii::t('app', 'Lang Title'),
+ 'language_id' => Yii::t('app', 'Lang ID'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getLanguage()
+ {
+ return $this->hasOne(Language::className(), ['language_id' => 'language_id']);
+ }
+}
diff --git a/frontend/models/Option.php b/frontend/models/Option.php
new file mode 100644
index 0000000..cfa62bb
--- /dev/null
+++ b/frontend/models/Option.php
@@ -0,0 +1,278 @@
+ 200]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'option_id' => Yii::t('app', 'Option ID'),
+ 'model' => Yii::t('app', 'Model'),
+ 'model_id' => Yii::t('app', 'Model ID'),
+ 'name' => Yii::t('app', 'Name'),
+ 'template' => Yii::t('app', 'Template'),
+ 'option_pid' => Yii::t('app', 'Parent ID'),
+ 'date_add' => Yii::t('app', 'Date created'),
+ 'translate' => Yii::t('app', 'Translatable'),
+ ];
+ }
+
+ public function getLangs() {
+ return (new Language())->find()->where(['>', 'language_id', 0])->andWhere(['status' => 1])->asArray()->all();
+ }
+
+ public static function change($id, $post, $modeldb, $model_id) {
+ $models[$id] = Option::findOne($id);
+ $modellang[$id] = array();
+ $langs = OptionLang::findAll(['option_language_id' => $id]);
+ foreach($langs as $lang) {
+ $modellang[$id][$lang->language_id] = $lang;
+ }
+ $children = (new Option())->find()->where(['option_pid' => $id])->all();
+ foreach($children as $child) {
+ $models[$child->option_id] = $child;
+ $modellang[$child->option_id] = array();
+ $langs = OptionLang::findAll(['option_id' =>$child->option_id]);
+ foreach($langs as $lang) {
+ $modellang[$child->option_id][$lang->language_id] = $lang;
+ }
+ }
+ $ok = 1;
+ if(!empty($post)) {
+ foreach($post['Option'] as $key => $option) {
+ if(in_array($key, array('model', 'model_id'))) { continue; }
+ if(empty($option['value'][$models[$key]->name]) && !empty($option['lang'])) {
+ foreach($option['lang'] as $language_id => $lang) {
+ if(!empty($lang)) {
+ $option['value'][$models[$key]->name] = $lang;
+ break;
+ }
+ }
+ }
+ $modellang[$key][0]->value = $option['value'][$models[$key]->name];
+ if(empty($modellang[$key][0]->value) || !$modellang[$key][0]->save()) {
+ $ok = 0;
+ $models[$key]->addError('value', 'Value must be set');
+ $modellang[$key][0]->addError('value', 'Value must be set');
+ }
+ if(!empty($option['lang'])) {
+ foreach($option['lang'] as $language_id => $lang) {
+ if(empty($modellang[$key][$language_id])) {
+ $modellang[$key][$language_id] = new OptionLang();
+ $modellang[$key][$language_id]->option_id = $models[$key]->option_id;
+ $modellang[$key][$language_id]->language_id = $language_id;
+ $modellang[$key][$language_id]->value = $lang;
+ } else {
+ $modellang[$key][$language_id]->value = $lang;
+ }
+ if(!$modellang[$key][$language_id]->save()) {
+ $ok = 0;
+ }
+ }
+ }
+ }
+ if($ok) {
+ return array('id' => $id, 'models' => $models, 'modelslang' => $modelslang, 'success' => true);
+ } else {
+ return array(
+ 'models' => $models,
+ 'modellang' => $modellang,
+ 'modeldb' => $modeldb,
+ 'model_id' => $model_id,
+ 'success' => false
+ );
+ }
+ }
+ return array(
+ 'models' => $models,
+ 'modellang' => $modellang,
+ 'modeldb' => $modeldb,
+ 'model_id' => $model_id,
+ 'success' => false
+ );
+ }
+
+ public static function create($post, $modeldb, $model_id, $fields, $multiple) {
+ $multiple = boolval($multiple);
+ $model = new Option();
+ $modellang = new OptionLang();
+ if(!empty($post['Option'])) {
+ $ok = 1;
+ $parentid = null;
+ $models = array();
+ foreach($post['Option'] as $index => $option) {
+ if(in_array($index, array('model', 'model_id')) && $index !== 0) { continue; }
+ $first = 1;
+ foreach($option['value'] as $key => $value) {
+ $models[$index][$key] = new Option();
+ $models[$index][$key]->model = $post['Option']['model'];
+ $models[$index][$key]->model_id = $post['Option']['model_id'];
+ $models[$index][$key]->template = $option[$key]['template'];
+ $models[$index][$key]->translate = $option[$key]['translate']?1:0;
+ $models[$index][$key]->name = $key;
+ if(!$first) {
+ $models[$index][$key]->option_pid = $parentid;
+ }
+ $modelslang[$index][$key][0] = new OptionLang();
+ if(!empty($option['lang'][$key])) {
+ foreach($option['lang'][$key] as $code => $lang) {
+ if(!empty($lang)) {
+ $value = $lang;
+ break;
+ }
+ }
+ }
+ if(!empty($value) && $models[$index][$key]->save()) {
+ if($first) {
+ $parentid = $models[$index][$key]->option_id;
+ }
+ $modelslang[$index][$key][0]->option_id = $models[$index][$key]->option_id;
+ $modelslang[$index][$key][0]->language_id = 0;
+ $modelslang[$index][$key][0]->value = $value;
+ if($modelslang[$index][$key][0]->save()) {
+ if(!empty($option['lang'][$key])) {
+ foreach($option['lang'][$key] as $code => $lang) {
+ if(!empty($lang)) {
+ $modelslang[$index][$key][$code] = new OptionLang();
+ $modelslang[$index][$key][$code]->option_id = $models[$index][$key]->option_id;
+ $modelslang[$index][$key][$code]->language_id = $code;
+ $modelslang[$index][$key][$code]->value = $lang;
+ if(!$modelslang[$index][$key][$code]->save()) {
+ $ok = 0;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ $models[$index][$key]->validate();
+ $modelslang[$index][$key][0]->validate();
+ $modelslang[$index][$key][0]->addError('value', 'Value must be set');
+ if(!empty($option['lang'][$key])) {
+ foreach($option['lang'][$key] as $code => $lang) {
+ if(!empty($lang)) {
+ $modelslang[$index][$key][$code] = new OptionLang();
+ $modelslang[$index][$key][$code]->option_id = $models[$index][$key]->option_id;
+ $modelslang[$index][$key][$code]->language_id = $code;
+ $modelslang[$index][$key][$code]->value = $lang;
+ }
+ }
+ }
+ $ok = 0;
+ }
+ $first = 0;
+ }
+ }
+ if($ok) {
+ if($modeldb == 'Feedback') {
+ $newflag = new Option();
+ $newflag->model = $modeldb;
+ $newflag->model_id = $model_id;
+ $newflag->name = 'is_new';
+ $newflag->template = 'checkbox';
+ $newflag->option_pid = $parentid;
+ $newflag->translate = 0;
+ if($newflag->save()) {
+ $newflaglang = new OptionLang();
+ $newflaglang->option_id = $newflag->option_id;
+ $newflaglang->language_id = 0;
+ $newflaglang->value = '1';
+ if(!$newflaglang->save()) {
+ $newflag->delete();
+ }
+ }
+ }
+ return array('models' => $models, 'modelslang' => $modelslang, 'success' => true);
+ } else {
+ return array(
+ 'models' => $models,
+ 'modellang' => $modelslang,
+ 'modeldb' => $modeldb,
+ 'model_id' => $model_id,
+ 'fields' => $fields,
+ 'success' => false,
+ 'multiple' => $multiple
+ );
+ }
+ } else {
+ return array(
+ 'model' => $model,
+ 'modeldb' => $modeldb,
+ 'model_id' => $model_id,
+ 'fields' => $fields,
+ 'success' => false,
+ 'multiple' => $multiple
+ );
+ }
+ }
+
+ public function getOptions() {
+ return $this->hasMany(Option::className(), ['option_pid' => 'option_id'])->indexBy('name');
+ }
+
+ public function getOption() {
+ return $this->hasOne(Option::className(), ['option_id' => 'option_pid']);
+ }
+
+ public function getOptionLangs() {
+ return $this->hasMany(OptionLang::className(), ['option_id' => 'option_id']);
+ }
+
+ public function getOptionDefaultLang($array = false) {
+ $query = $this->getOptionLangs()->where(['language_id' => 0]);
+ if($array) {
+ $query->asArray();
+ }
+ return $query->one();
+ }
+
+ public static function markOld($id) {
+ $model = Option::findOne($id);
+ $is_new = $model->getOptions()->where(['name' => 'is_new'])->one();
+ if(empty($is_new)) return false;
+ $is_newlang = $is_new->getOptionDefaultLang();
+ $is_newlang->value = '0';
+ if($is_newlang->save()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/frontend/models/OptionLang.php b/frontend/models/OptionLang.php
new file mode 100644
index 0000000..5b98585
--- /dev/null
+++ b/frontend/models/OptionLang.php
@@ -0,0 +1,47 @@
+ Yii::t('app', 'ID'),
+ 'language_id' => Yii::t('app', 'Lang ID'),
+ 'value' => Yii::t('app', 'Value'),
+ ];
+ }
+}
diff --git a/frontend/models/OptionLangSearch.php b/frontend/models/OptionLangSearch.php
new file mode 100644
index 0000000..4ba6e1c
--- /dev/null
+++ b/frontend/models/OptionLangSearch.php
@@ -0,0 +1,68 @@
+ $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;
+ }
+
+ $query->andFilterWhere([
+ 'option_language_id' => $this->option_language_id,
+ 'option_id' => $this->option_id,
+ 'language_id' => $this->language_id,
+ ]);
+
+ $query->andFilterWhere(['like', 'value', $this->value]);
+
+ return $dataProvider;
+ }
+}
diff --git a/frontend/models/OptionSearch.php b/frontend/models/OptionSearch.php
new file mode 100644
index 0000000..984970c
--- /dev/null
+++ b/frontend/models/OptionSearch.php
@@ -0,0 +1,78 @@
+ $query,
+ ]);
+ $data = [
+ 'OptionSearch' => [
+ 'model' => 'Main'
+ ]
+ ];
+
+ $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;
+ }
+
+ $query->andWhere(['option_pid' => null]);
+
+ $query->andFilterWhere([
+ 'option_id' => $this->option_id,
+ 'model_id' => $this->model_id,
+ 'option_pid' => $this->option_pid,
+ ]);
+
+ $query->andFilterWhere(['like', 'model', $this->model])
+ ->andFilterWhere(['like', 'name', $this->name])
+ ->andFilterWhere(['like', 'template', $this->template]);
+
+
+ return $dataProvider;
+ }
+}
diff --git a/frontend/models/OptionValues.php b/frontend/models/OptionValues.php
new file mode 100644
index 0000000..7ab4e2b
--- /dev/null
+++ b/frontend/models/OptionValues.php
@@ -0,0 +1,97 @@
+ 200]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'option_value_id' => Yii::t('app', 'Option Value ID'),
+ 'option_key' => Yii::t('app', 'Option Key'),
+ 'option_value_text' => Yii::t('app', 'Option Value Text'),
+ 'option_language_id' => Yii::t('app', 'Option Lang ID'),
+ 'option_value_parent' => Yii::t('app', 'Option Value Parent'),
+ 'option_user' => Yii::t('app', 'Option User'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getOptionLang()
+ {
+ return $this->hasOne(Language::className(), ['language_id' => 'option_language_id']);
+ }
+ public function getLanguages() {
+ return (new LanguageLang())->find()->orderBy('language_id ASC')->asArray()->all();
+ }
+ public function getLanguagesCodes() {
+ return (new Language())->find()->orderBy('language_id ASC')->asArray()->all();
+ }
+ public function getDropDownArray() {
+ $langs = array();
+ foreach($this->getLanguages() as $lang) {
+ $langs[$lang['language_id']] = $lang['lang_title'];
+ }
+ return $langs;
+ }
+ public function beforeSave($insert) {
+ if (parent::beforeSave($insert)) {
+ $this->option_user = \Yii::$app->user->getId();
+ if($this->option_value_parent == 0) {
+ unset($this->option_value_parent);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ public function getUserOptions() {
+ return (new OptionValues())->find()->where('option_user=:user')->addParams([':user' => \Yii::$app->user->getID()])->andWhere(['not', ['option_user' => null]])->asArray()->all();
+ }
+ public function getUserOptionsArray() {
+ $options = array('0' => Yii::t('app', 'Default Parent'));
+ foreach($this->getUserOptions() as $option) {
+ $options[$option['option_value_id']] = $option['option_key'].' - '.$option['option_value_text'];
+ }
+ return $options;
+ }
+}
diff --git a/frontend/models/OptionValuesSearch.php b/frontend/models/OptionValuesSearch.php
new file mode 100644
index 0000000..e8dafed
--- /dev/null
+++ b/frontend/models/OptionValuesSearch.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;
+ }
+
+ $query->andFilterWhere([
+ 'option_value_id' => $this->option_value_id,
+ 'option_language_id' => $this->option_language_id,
+ 'option_value_parent' => $this->option_value_parent,
+ 'option_user' => $this->option_user,
+ ]);
+
+ $query->andFilterWhere(['like', 'option_key', $this->option_key])
+ ->andFilterWhere(['like', 'option_value_text', $this->option_value_text]);
+
+ return $dataProvider;
+ }
+}
diff --git a/frontend/models/Options.php b/frontend/models/Options.php
new file mode 100644
index 0000000..8efac0c
--- /dev/null
+++ b/frontend/models/Options.php
@@ -0,0 +1,78 @@
+ 200]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels ()
+ {
+ return [
+ 'option_id' => Yii::t ('app', 'Option ID'), 'option_key' => Yii::t ('app', 'Option Key'), 'option_parent' => Yii::t ('app', 'Option Parent'), 'option_translatable' => Yii::t ('app', 'Option Translatable'), 'option_format' => Yii::t ('app', 'Option Format'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getOptionLang ()
+ {
+ return $this->hasMany (OptionLang::className (), ['option_id' => 'option_id']);
+ }
+
+ public function getValue ()
+ {
+ return $this->hasOne(OptionLang::className(), ['option_id' => 'option_id'])->where(['option_lang.language_id' => '0']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getOptionParent()
+ {
+ return $this->hasOne(Options::className(), ['option_id' => 'option_parent']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getOptions()
+ {
+ return $this->hasMany(Options::className(), ['option_parent' => 'option_id']);
+ }
+}
diff --git a/frontend/models/OptionsSearch.php b/frontend/models/OptionsSearch.php
new file mode 100644
index 0000000..0fbac03
--- /dev/null
+++ b/frontend/models/OptionsSearch.php
@@ -0,0 +1,69 @@
+ $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;
+ }
+
+ $query->andFilterWhere([
+ 'option_id' => $this->option_id,
+ 'option_parent' => $this->option_parent,
+ 'option_translatable' => $this->option_translatable,
+ ]);
+
+ $query->andFilterWhere(['like', 'option_key', $this->option_key])
+ ->andFilterWhere(['like', 'option_format', $this->option_format]);
+
+ return $dataProvider;
+ }
+}
diff --git a/frontend/models/PasswordResetRequestForm.php b/frontend/models/PasswordResetRequestForm.php
new file mode 100644
index 0000000..20c6810
--- /dev/null
+++ b/frontend/models/PasswordResetRequestForm.php
@@ -0,0 +1,60 @@
+ 'trim'],
+ ['email', 'required'],
+ ['email', 'email'],
+ ['email', 'exist',
+ 'targetClass' => '\common\models\User',
+ 'filter' => ['status' => User::STATUS_ACTIVE],
+ 'message' => 'There is no user with such email.'
+ ],
+ ];
+ }
+
+ /**
+ * Sends an email with a link, for resetting the password.
+ *
+ * @return boolean whether the email was send
+ */
+ public function sendEmail()
+ {
+ /* @var $user User */
+ $user = User::findOne([
+ 'status' => User::STATUS_ACTIVE,
+ 'email' => $this->email,
+ ]);
+
+ if ($user) {
+ if (!User::isPasswordResetTokenValid($user->password_reset_token)) {
+ $user->generatePasswordResetToken();
+ }
+
+ if ($user->save()) {
+ 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();
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/frontend/models/ResetPasswordForm.php b/frontend/models/ResetPasswordForm.php
new file mode 100644
index 0000000..dd48f52
--- /dev/null
+++ b/frontend/models/ResetPasswordForm.php
@@ -0,0 +1,65 @@
+_user = User::findByPasswordResetToken($token);
+ if (!$this->_user) {
+ throw new InvalidParamException('Wrong password reset token.');
+ }
+ parent::__construct($config);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ ['password', 'required'],
+ ['password', 'string', 'min' => 6],
+ ];
+ }
+
+ /**
+ * Resets password.
+ *
+ * @return boolean if password was reset.
+ */
+ public function resetPassword()
+ {
+ $user = $this->_user;
+ $user->setPassword($this->password);
+ $user->removePasswordResetToken();
+
+ return $user->save(false);
+ }
+}
diff --git a/frontend/models/SignupForm.php b/frontend/models/SignupForm.php
new file mode 100644
index 0000000..a894a5e
--- /dev/null
+++ b/frontend/models/SignupForm.php
@@ -0,0 +1,60 @@
+ 'trim'],
+ ['username', 'required'],
+ ['username', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This username has already been taken.'],
+ ['username', 'string', 'min' => 2, 'max' => 255],
+
+ ['email', 'filter', 'filter' => 'trim'],
+ ['email', 'required'],
+ ['email', 'email'],
+ ['email', 'string', 'max' => 255],
+ ['email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken.'],
+
+ ['password', 'required'],
+ ['password', 'string', 'min' => 6],
+ ];
+ }
+
+ /**
+ * Signs user up.
+ *
+ * @return User|null the saved model or null if saving fails
+ */
+ public function signup()
+ {
+ if ($this->validate()) {
+ $user = new User();
+ $user->username = $this->username;
+ $user->email = $this->email;
+ $user->setPassword($this->password);
+ $user->generateAuthKey();
+ if ($user->save()) {
+ return $user;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/frontend/runtime/.gitignore b/frontend/runtime/.gitignore
new file mode 100644
index 0000000..c96a04f
--- /dev/null
+++ b/frontend/runtime/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/frontend/views/catalog/index.php b/frontend/views/catalog/index.php
new file mode 100644
index 0000000..d89d2f1
--- /dev/null
+++ b/frontend/views/catalog/index.php
@@ -0,0 +1,6 @@
+build();
\ No newline at end of file
diff --git a/frontend/views/layouts/main.php b/frontend/views/layouts/main.php
new file mode 100644
index 0000000..8f8b8d5
--- /dev/null
+++ b/frontend/views/layouts/main.php
@@ -0,0 +1,102 @@
+
+beginPage() ?>
+
+
+
+
+
+ = Html::csrfMetaTags() ?>
+ = Html::encode($this->title) ?>
+ head() ?>
+
+
+beginBody() ?>
+
+
+ [
+ 'class' => 'navbar-inverse navbar-fixed-top',
+ ]
+ ]);
+
+ echo Nav::widget([
+ 'options' => ['class' => 'navbar-nav navbar-right'],
+ 'items' => (new MenuTree())->build('TOP')
+ ]);
+/*
+
+ $menuItems = [
+ ['label' => 'Home', 'url' => ['/site/index']],
+ ['label' => 'About', 'url' => ['/site/about']],
+ ['label' => 'Contact', 'url' => ['/site/contact']],
+ ];
+
+ // меню с базы
+ $menuItems = [];
+
+ foreach ((new \frontend\models\Front())->actionMenu('TOP') as $row)
+ {
+ $menuItems[] = ['label' => $row['page_title'], 'url' => $row['page_alias']];
+ }
+
+ if (Yii::$app->user->isGuest)
+ {
+ $menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']];
+ $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];
+ }
+ else
+ {
+ $menuItems[] = [
+ 'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
+ 'url' => ['/site/logout'],
+ 'linkOptions' => ['data-method' => 'post']
+ ];
+ }
+
+ echo Nav::widget([
+ 'options' => ['class' => 'navbar-nav navbar-right'],
+ 'items' => $menuItems,
+ ]);
+*/
+ NavBar::end();
+
+ ?>
+
+
+
+ = Breadcrumbs::widget([
+ 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
+ ]) ?>
+ = Alert::widget() ?>
+ = $content ?>
+
+
+
+
+
+endBody() ?>
+
+
+endPage() ?>
diff --git a/frontend/views/option-values/_form.php b/frontend/views/option-values/_form.php
new file mode 100644
index 0000000..3d9f0d6
--- /dev/null
+++ b/frontend/views/option-values/_form.php
@@ -0,0 +1,60 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/views/option-values/_search.php b/frontend/views/option-values/_search.php
new file mode 100644
index 0000000..6269471
--- /dev/null
+++ b/frontend/views/option-values/_search.php
@@ -0,0 +1,37 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'option_value_id') ?>
+
+ = $form->field($model, 'option_key') ?>
+
+ = $form->field($model, 'option_value_text') ?>
+
+ = $form->field($model, 'option_language_id') ?>
+
+ = $form->field($model, 'option_value_parent') ?>
+
+ field($model, 'option_user') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/frontend/views/option-values/create.php b/frontend/views/option-values/create.php
new file mode 100644
index 0000000..6bfcaad
--- /dev/null
+++ b/frontend/views/option-values/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Create Option Values');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Option Values'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/frontend/views/option-values/index.php b/frontend/views/option-values/index.php
new file mode 100644
index 0000000..98160c2
--- /dev/null
+++ b/frontend/views/option-values/index.php
@@ -0,0 +1,39 @@
+title = Yii::t('app', 'Option Values');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create Option Values'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'option_value_id',
+ 'option_key',
+ 'option_value_text:ntext',
+ 'option_language_id',
+ 'option_value_parent',
+ // 'option_user',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+
diff --git a/frontend/views/option-values/update.php b/frontend/views/option-values/update.php
new file mode 100644
index 0000000..b993f40
--- /dev/null
+++ b/frontend/views/option-values/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Option Values',
+]) . ' ' . $model->option_value_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Option Values'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->option_value_id, 'url' => ['view', 'id' => $model->option_value_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/frontend/views/option-values/view.php b/frontend/views/option-values/view.php
new file mode 100644
index 0000000..93b6f5f
--- /dev/null
+++ b/frontend/views/option-values/view.php
@@ -0,0 +1,40 @@
+title = $model->option_value_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Option Values'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->option_value_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->option_value_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'option_value_id',
+ 'option_key',
+ 'option_value_text:ntext',
+ 'option_language_id',
+ 'option_value_parent',
+ 'option_user',
+ ],
+ ]) ?>
+
+
diff --git a/frontend/views/option/_form.php b/frontend/views/option/_form.php
new file mode 100644
index 0000000..ed88827
--- /dev/null
+++ b/frontend/views/option/_form.php
@@ -0,0 +1,128 @@
+
+
+
diff --git a/frontend/views/option/_form_edit.php b/frontend/views/option/_form_edit.php
new file mode 100644
index 0000000..f5384ce
--- /dev/null
+++ b/frontend/views/option/_form_edit.php
@@ -0,0 +1,41 @@
+
+
+
diff --git a/frontend/views/option/_search.php b/frontend/views/option/_search.php
new file mode 100644
index 0000000..5fe0200
--- /dev/null
+++ b/frontend/views/option/_search.php
@@ -0,0 +1,37 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'option_id') ?>
+
+ = $form->field($model, 'model') ?>
+
+ = $form->field($model, 'model_id') ?>
+
+ = $form->field($model, 'name') ?>
+
+ = $form->field($model, 'template') ?>
+
+ field($model, 'parent_id') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/frontend/views/option/create.php b/frontend/views/option/create.php
new file mode 100644
index 0000000..7c128cc
--- /dev/null
+++ b/frontend/views/option/create.php
@@ -0,0 +1,19 @@
+title = Yii::t('app', 'Create Option');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+ render('_form', $oneform);
+ }?>
+
+
diff --git a/frontend/views/option/index.php b/frontend/views/option/index.php
new file mode 100644
index 0000000..9051bb3
--- /dev/null
+++ b/frontend/views/option/index.php
@@ -0,0 +1,39 @@
+title = Yii::t('app', 'Options');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create Option'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'option_id',
+ 'model',
+ 'model_id',
+ 'name',
+ 'template',
+ // 'parent_id',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+
diff --git a/frontend/views/option/update.php b/frontend/views/option/update.php
new file mode 100644
index 0000000..1bb652d
--- /dev/null
+++ b/frontend/views/option/update.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Option',
+]) . ' ' . $forms[0]['models'][\Yii::$app->request->get('id')]->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $forms[0]['models'][\Yii::$app->request->get('id')]->name, 'url' => ['view', 'id' => $forms[0]['models'][\Yii::$app->request->get('id')]->option_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
= Html::encode($this->title) ?>
+ render('_form_edit', $oneform);
+ }?>
+
+
diff --git a/frontend/views/option/view.php b/frontend/views/option/view.php
new file mode 100644
index 0000000..ba4d20b
--- /dev/null
+++ b/frontend/views/option/view.php
@@ -0,0 +1,40 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->option_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->option_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'option_id',
+ 'model',
+ 'model_id',
+ 'name',
+ 'template',
+ 'parent_id',
+ ],
+ ]) ?>
+
+
diff --git a/frontend/views/options/_form.php b/frontend/views/options/_form.php
new file mode 100644
index 0000000..0cfe921
--- /dev/null
+++ b/frontend/views/options/_form.php
@@ -0,0 +1,29 @@
+
+
+
diff --git a/frontend/views/options/_search.php b/frontend/views/options/_search.php
new file mode 100644
index 0000000..eab038c
--- /dev/null
+++ b/frontend/views/options/_search.php
@@ -0,0 +1,35 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'option_id') ?>
+
+ = $form->field($model, 'option_key') ?>
+
+ = $form->field($model, 'option_parent') ?>
+
+ = $form->field($model, 'option_translatable') ?>
+
+ = $form->field($model, 'option_format') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/frontend/views/options/create.php b/frontend/views/options/create.php
new file mode 100644
index 0000000..95f6f60
--- /dev/null
+++ b/frontend/views/options/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Create Options');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/frontend/views/options/index.php b/frontend/views/options/index.php
new file mode 100644
index 0000000..255f2fd
--- /dev/null
+++ b/frontend/views/options/index.php
@@ -0,0 +1,38 @@
+title = Yii::t('app', 'Options');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create Options'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'option_id',
+ 'option_key',
+ 'option_parent',
+ 'option_translatable',
+ 'option_format',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+
diff --git a/frontend/views/options/update.php b/frontend/views/options/update.php
new file mode 100644
index 0000000..e3c092a
--- /dev/null
+++ b/frontend/views/options/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'Options',
+]) . ' ' . $model->option_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->option_id, 'url' => ['view', 'id' => $model->option_id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/frontend/views/options/view.php b/frontend/views/options/view.php
new file mode 100644
index 0000000..6368d9c
--- /dev/null
+++ b/frontend/views/options/view.php
@@ -0,0 +1,39 @@
+title = $model->option_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Options'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->option_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->option_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'option_id',
+ 'option_key',
+ 'option_parent',
+ 'option_translatable',
+ 'option_format',
+ ],
+ ]) ?>
+
+
diff --git a/frontend/views/site/about.php b/frontend/views/site/about.php
new file mode 100644
index 0000000..8eb0764
--- /dev/null
+++ b/frontend/views/site/about.php
@@ -0,0 +1,16 @@
+title = 'About';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+
+
This is the About page. You may modify the following file to customize its content:
+
+
= __FILE__ ?>
+
diff --git a/frontend/views/site/contact.php b/frontend/views/site/contact.php
new file mode 100644
index 0000000..16ebdb2
--- /dev/null
+++ b/frontend/views/site/contact.php
@@ -0,0 +1,45 @@
+title = 'Contact';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
diff --git a/frontend/views/site/error.php b/frontend/views/site/error.php
new file mode 100644
index 0000000..0ba2574
--- /dev/null
+++ b/frontend/views/site/error.php
@@ -0,0 +1,27 @@
+title = $name;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = nl2br(Html::encode($message)) ?>
+
+
+
+ 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/feedback.php b/frontend/views/site/feedback.php
new file mode 100644
index 0000000..9527ec5
--- /dev/null
+++ b/frontend/views/site/feedback.php
@@ -0,0 +1,19 @@
+title = Yii::t('app', 'Feedback');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+ render('/option/_form', $oneform);
+ }?>
+
diff --git a/frontend/views/site/index.php b/frontend/views/site/index.php
new file mode 100644
index 0000000..0abef59
--- /dev/null
+++ b/frontend/views/site/index.php
@@ -0,0 +1,54 @@
+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 100644
index 0000000..746a527
--- /dev/null
+++ b/frontend/views/site/login.php
@@ -0,0 +1,33 @@
+title = 'Login';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+title); ?>
+
+getSession()->hasFlash('error')) {
+ echo ''.Yii::$app->getSession()->getFlash('error').'
';
+ }
+?>
+
+Do you already have an account on one of these sites? Click the logo to log in with it here:
+ 'site/login')); ?>
+
+
+Please fill out the following fields to login:
+
+ array('class' => 'form-horizontal', 'id' => 'login-form'))); ?>
+ field($model, 'username')->textInput(); ?>
+ field($model, 'password')->passwordInput(); ?>
+ field($model, 'rememberMe')->checkbox(); ?>
+
+ 'btn btn-primary')); ?>
+
+
\ No newline at end of file
diff --git a/frontend/views/site/options.php b/frontend/views/site/options.php
new file mode 100644
index 0000000..80a8ce6
--- /dev/null
+++ b/frontend/views/site/options.php
@@ -0,0 +1,24 @@
+title = 'My Yii Application';
+?>
+
+
+
\ No newline at end of file
diff --git a/frontend/views/site/requestPasswordResetToken.php b/frontend/views/site/requestPasswordResetToken.php
new file mode 100644
index 0000000..494ddb3
--- /dev/null
+++ b/frontend/views/site/requestPasswordResetToken.php
@@ -0,0 +1,31 @@
+title = 'Request password reset';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+
+
Please fill out your email. A link to reset password will be sent there.
+
+
+
+ 'request-password-reset-form']); ?>
+
+ = $form->field($model, 'email') ?>
+
+
+ = Html::submitButton('Send', ['class' => 'btn btn-primary']) ?>
+
+
+
+
+
+
diff --git a/frontend/views/site/resetPassword.php b/frontend/views/site/resetPassword.php
new file mode 100644
index 0000000..8e6d93f
--- /dev/null
+++ b/frontend/views/site/resetPassword.php
@@ -0,0 +1,31 @@
+title = 'Reset password';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+
+
Please choose your new password:
+
+
+
+ 'reset-password-form']); ?>
+
+ = $form->field($model, 'password')->passwordInput() ?>
+
+
+ = Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
+
+
+
+
+
+
diff --git a/frontend/views/site/signup.php b/frontend/views/site/signup.php
new file mode 100644
index 0000000..58ccd8e
--- /dev/null
+++ b/frontend/views/site/signup.php
@@ -0,0 +1,35 @@
+title = 'Signup';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+
+
Please fill out the following fields to signup:
+
+
+
+ 'form-signup']); ?>
+
+ = $form->field($model, 'username') ?>
+
+ = $form->field($model, 'email') ?>
+
+ = $form->field($model, 'password')->passwordInput() ?>
+
+
+ = Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?>
+
+
+
+
+
+
diff --git a/frontend/web/.gitignore b/frontend/web/.gitignore
new file mode 100644
index 0000000..25c74e6
--- /dev/null
+++ b/frontend/web/.gitignore
@@ -0,0 +1,2 @@
+/index.php
+/index-test.php
diff --git a/frontend/web/assets/.gitignore b/frontend/web/assets/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/frontend/web/assets/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/frontend/web/css/flags32.css b/frontend/web/css/flags32.css
new file mode 100644
index 0000000..ba24b4f
--- /dev/null
+++ b/frontend/web/css/flags32.css
@@ -0,0 +1,253 @@
+.f32 .flag{display:inline-block;height:32px;width:32px;vertical-align:text-top;line-height:32px;background:url(../images/flags32.png) no-repeat;}
+.f32 ._African_Union{background-position:0 -32px;}
+.f32 ._Arab_League{background-position:0 -64px;}
+.f32 ._ASEAN{background-position:0 -96px;}
+.f32 ._CARICOM{background-position:0 -128px;}
+.f32 ._CIS{background-position:0 -160px;}
+.f32 ._Commonwealth{background-position:0 -192px;}
+.f32 ._England{background-position:0 -224px;}
+.f32 ._European_Union, .f32 .eu{background-position:0 -256px;}
+.f32 ._Islamic_Conference{background-position:0 -288px;}
+.f32 ._Kosovo{background-position:0 -320px;}
+.f32 ._NATO{background-position:0 -352px;}
+.f32 ._Northern_Cyprus{background-position:0 -384px;}
+.f32 ._Northern_Ireland{background-position:0 -416px;}
+.f32 ._Olimpic_Movement{background-position:0 -448px;}
+.f32 ._OPEC{background-position:0 -480px;}
+.f32 ._Red_Cross{background-position:0 -512px;}
+.f32 ._Scotland{background-position:0 -544px;}
+.f32 ._Somaliland{background-position:0 -576px;}
+.f32 ._Tibet{background-position:0 -608px;}
+.f32 ._United_Nations{background-position:0 -640px;}
+.f32 ._Wales{background-position:0 -672px;}
+.f32 .ad{background-position:0 -704px;}
+.f32 .ae{background-position:0 -736px;}
+.f32 .af{background-position:0 -768px;}
+.f32 .ag{background-position:0 -800px;}
+.f32 .ai{background-position:0 -832px;}
+.f32 .al{background-position:0 -864px;}
+.f32 .am{background-position:0 -896px;}
+.f32 .ao{background-position:0 -928px;}
+.f32 .aq{background-position:0 -960px;}
+.f32 .ar{background-position:0 -992px;}
+.f32 .as{background-position:0 -1024px;}
+.f32 .at{background-position:0 -1056px;}
+.f32 .au{background-position:0 -1088px;}
+.f32 .aw{background-position:0 -1120px;}
+.f32 .ax{background-position:0 -1152px;}
+.f32 .az{background-position:0 -1184px;}
+.f32 .ba{background-position:0 -1216px;}
+.f32 .bb{background-position:0 -1248px;}
+.f32 .bd{background-position:0 -1280px;}
+.f32 .be{background-position:0 -1312px;}
+.f32 .bf{background-position:0 -1344px;}
+.f32 .bg{background-position:0 -1376px;}
+.f32 .bh{background-position:0 -1408px;}
+.f32 .bi{background-position:0 -1440px;}
+.f32 .bj{background-position:0 -1472px;}
+.f32 .bm{background-position:0 -1504px;}
+.f32 .bn{background-position:0 -1536px;}
+.f32 .bo{background-position:0 -1568px;}
+.f32 .br{background-position:0 -1600px;}
+.f32 .bs{background-position:0 -1632px;}
+.f32 .bt{background-position:0 -1664px;}
+.f32 .bw{background-position:0 -1696px;}
+.f32 .by{background-position:0 -1728px;}
+.f32 .bz{background-position:0 -1760px;}
+.f32 .ca{background-position:0 -1792px;}
+.f32 .cd{background-position:0 -1824px;}
+.f32 .cf{background-position:0 -1856px;}
+.f32 .cg{background-position:0 -1888px;}
+.f32 .ch{background-position:0 -1920px;}
+.f32 .ci{background-position:0 -1952px;}
+.f32 .ck{background-position:0 -1984px;}
+.f32 .cl{background-position:0 -2016px;}
+.f32 .cm{background-position:0 -2048px;}
+.f32 .cn{background-position:0 -2080px;}
+.f32 .co{background-position:0 -2112px;}
+.f32 .cr{background-position:0 -2144px;}
+.f32 .cu{background-position:0 -2176px;}
+.f32 .cv{background-position:0 -2208px;}
+.f32 .cy{background-position:0 -2240px;}
+.f32 .cz{background-position:0 -2272px;}
+.f32 .de{background-position:0 -2304px;}
+.f32 .dj{background-position:0 -2336px;}
+.f32 .dk{background-position:0 -2368px;}
+.f32 .dm{background-position:0 -2400px;}
+.f32 .do{background-position:0 -2432px;}
+.f32 .dz{background-position:0 -2464px;}
+.f32 .ec{background-position:0 -2496px;}
+.f32 .ee{background-position:0 -2528px;}
+.f32 .eg{background-position:0 -2560px;}
+.f32 .eh{background-position:0 -2592px;}
+.f32 .er{background-position:0 -2624px;}
+.f32 .es{background-position:0 -2656px;}
+.f32 .et{background-position:0 -2688px;}
+.f32 .fi{background-position:0 -2720px;}
+.f32 .fj{background-position:0 -2752px;}
+.f32 .fm{background-position:0 -2784px;}
+.f32 .fo{background-position:0 -2816px;}
+.f32 .fr{background-position:0 -2848px;} .f32 .bl, .f32 .cp, .f32 .mf, .f32 .yt{background-position:0 -2848px;}
+.f32 .ga{background-position:0 -2880px;}
+.f32 .gb{background-position:0 -2912px;} .f32 .sh{background-position:0 -2912px;}
+.f32 .gd{background-position:0 -2944px;}
+.f32 .ge{background-position:0 -2976px;}
+.f32 .gg{background-position:0 -3008px;}
+.f32 .gh{background-position:0 -3040px;}
+.f32 .gi{background-position:0 -3072px;}
+.f32 .gl{background-position:0 -3104px;}
+.f32 .gm{background-position:0 -3136px;}
+.f32 .gn{background-position:0 -3168px;}
+.f32 .gp{background-position:0 -3200px;}
+.f32 .gq{background-position:0 -3232px;}
+.f32 .gr{background-position:0 -3264px;}
+.f32 .gt{background-position:0 -3296px;}
+.f32 .gu{background-position:0 -3328px;}
+.f32 .gw{background-position:0 -3360px;}
+.f32 .gy{background-position:0 -3392px;}
+.f32 .hk{background-position:0 -3424px;}
+.f32 .hn{background-position:0 -3456px;}
+.f32 .hr{background-position:0 -3488px;}
+.f32 .ht{background-position:0 -3520px;}
+.f32 .hu{background-position:0 -3552px;}
+.f32 .id{background-position:0 -3584px;}
+.f32 .mc{background-position:0 -3584px;}
+.f32 .ie{background-position:0 -3616px;}
+.f32 .il{background-position:0 -3648px;}
+.f32 .im{background-position:0 -3680px;}
+.f32 .in{background-position:0 -3712px;}
+.f32 .iq{background-position:0 -3744px;}
+.f32 .ir{background-position:0 -3776px;}
+.f32 .is{background-position:0 -3808px;}
+.f32 .it{background-position:0 -3840px;}
+.f32 .je{background-position:0 -3872px;}
+.f32 .jm{background-position:0 -3904px;}
+.f32 .jo{background-position:0 -3936px;}
+.f32 .jp{background-position:0 -3968px;}
+.f32 .ke{background-position:0 -4000px;}
+.f32 .kg{background-position:0 -4032px;}
+.f32 .kh{background-position:0 -4064px;}
+.f32 .ki{background-position:0 -4096px;}
+.f32 .km{background-position:0 -4128px;}
+.f32 .kn{background-position:0 -4160px;}
+.f32 .kp{background-position:0 -4192px;}
+.f32 .kr{background-position:0 -4224px;}
+.f32 .kw{background-position:0 -4256px;}
+.f32 .ky{background-position:0 -4288px;}
+.f32 .kz{background-position:0 -4320px;}
+.f32 .la{background-position:0 -4352px;}
+.f32 .lb{background-position:0 -4384px;}
+.f32 .lc{background-position:0 -4416px;}
+.f32 .li{background-position:0 -4448px;}
+.f32 .lk{background-position:0 -4480px;}
+.f32 .lr{background-position:0 -4512px;}
+.f32 .ls{background-position:0 -4544px;}
+.f32 .lt{background-position:0 -4576px;}
+.f32 .lu{background-position:0 -4608px;}
+.f32 .lv{background-position:0 -4640px;}
+.f32 .ly{background-position:0 -4672px;}
+.f32 .ma{background-position:0 -4704px;}
+.f32 .md{background-position:0 -4736px;}
+.f32 .me{background-position:0 -4768px;}
+.f32 .mg{background-position:0 -4800px;}
+.f32 .mh{background-position:0 -4832px;}
+.f32 .mk{background-position:0 -4864px;}
+.f32 .ml{background-position:0 -4896px;}
+.f32 .mm{background-position:0 -4928px;}
+.f32 .mn{background-position:0 -4960px;}
+.f32 .mo{background-position:0 -4992px;}
+.f32 .mq{background-position:0 -5024px;}
+.f32 .mr{background-position:0 -5056px;}
+.f32 .ms{background-position:0 -5088px;}
+.f32 .mt{background-position:0 -5120px;}
+.f32 .mu{background-position:0 -5152px;}
+.f32 .mv{background-position:0 -5184px;}
+.f32 .mw{background-position:0 -5216px;}
+.f32 .mx{background-position:0 -5248px;}
+.f32 .my{background-position:0 -5280px;}
+.f32 .mz{background-position:0 -5312px;}
+.f32 .na{background-position:0 -5344px;}
+.f32 .nc{background-position:0 -5376px;}
+.f32 .ne{background-position:0 -5408px;}
+.f32 .ng{background-position:0 -5440px;}
+.f32 .ni{background-position:0 -5472px;}
+.f32 .nl{background-position:0 -5504px;} .f32 .bq{background-position:0 -5504px;}
+.f32 .no{background-position:0 -5536px;} .f32 .bv, .f32 .nq, .f32 .sj{background-position:0 -5536px;}
+.f32 .np{background-position:0 -5568px;}
+.f32 .nr{background-position:0 -5600px;}
+.f32 .nz{background-position:0 -5632px;}
+.f32 .om{background-position:0 -5664px;}
+.f32 .pa{background-position:0 -5696px;}
+.f32 .pe{background-position:0 -5728px;}
+.f32 .pf{background-position:0 -5760px;}
+.f32 .pg{background-position:0 -5792px;}
+.f32 .ph{background-position:0 -5824px;}
+.f32 .pk{background-position:0 -5856px;}
+.f32 .pl{background-position:0 -5888px;}
+.f32 .pr{background-position:0 -5920px;}
+.f32 .ps{background-position:0 -5952px;}
+.f32 .pt{background-position:0 -5984px;}
+.f32 .pw{background-position:0 -6016px;}
+.f32 .py{background-position:0 -6048px;}
+.f32 .qa{background-position:0 -6080px;}
+.f32 .re{background-position:0 -6112px;}
+.f32 .ro{background-position:0 -6144px;}
+.f32 .rs{background-position:0 -6176px;}
+.f32 .ru{background-position:0 -6208px;}
+.f32 .rw{background-position:0 -6240px;}
+.f32 .sa{background-position:0 -6272px;}
+.f32 .sb{background-position:0 -6304px;}
+.f32 .sc{background-position:0 -6336px;}
+.f32 .sd{background-position:0 -6368px;}
+.f32 .se{background-position:0 -6400px;}
+.f32 .sg{background-position:0 -6432px;}
+.f32 .si{background-position:0 -6464px;}
+.f32 .sk{background-position:0 -6496px;}
+.f32 .sl{background-position:0 -6528px;}
+.f32 .sm{background-position:0 -6560px;}
+.f32 .sn{background-position:0 -6592px;}
+.f32 .so{background-position:0 -6624px;}
+.f32 .sr{background-position:0 -6656px;}
+.f32 .st{background-position:0 -6688px;}
+.f32 .sv{background-position:0 -6720px;}
+.f32 .sy{background-position:0 -6752px;}
+.f32 .sz{background-position:0 -6784px;}
+.f32 .tc{background-position:0 -6816px;}
+.f32 .td{background-position:0 -6848px;}
+.f32 .tg{background-position:0 -6880px;}
+.f32 .th{background-position:0 -6912px;}
+.f32 .tj{background-position:0 -6944px;}
+.f32 .tl{background-position:0 -6976px;}
+.f32 .tm{background-position:0 -7008px;}
+.f32 .tn{background-position:0 -7040px;}
+.f32 .to{background-position:0 -7072px;}
+.f32 .tr{background-position:0 -7104px;}
+.f32 .tt{background-position:0 -7136px;}
+.f32 .tv{background-position:0 -7168px;}
+.f32 .tw{background-position:0 -7200px;}
+.f32 .tz{background-position:0 -7232px;}
+.f32 .ua{background-position:0 -7264px;}
+.f32 .ug{background-position:0 -7296px;}
+.f32 .us{background-position:0 -7328px;}
+.f32 .uy{background-position:0 -7360px;}
+.f32 .uz{background-position:0 -7392px;}
+.f32 .va{background-position:0 -7424px;}
+.f32 .vc{background-position:0 -7456px;}
+.f32 .ve{background-position:0 -7488px;}
+.f32 .vg{background-position:0 -7520px;}
+.f32 .vi{background-position:0 -7552px;}
+.f32 .vn{background-position:0 -7584px;}
+.f32 .vu{background-position:0 -7616px;}
+.f32 .ws{background-position:0 -7648px;}
+.f32 .ye{background-position:0 -7680px;}
+.f32 .za{background-position:0 -7712px;}
+.f32 .zm{background-position:0 -7744px;}
+.f32 .zw{background-position:0 -7776px;}
+.f32 .sx{background-position:0 -7808px;}
+.f32 .cw{background-position:0 -7840px;}
+.f32 .ss{background-position:0 -7872px;}
+<<<<<<< 1fd2bdb43fc5cfdcf100cac8b72e67fd81e7f0fa
+.f32 .nu{background-position:0 -7904px;}
+=======
+.f32 .nu{background-position:0 -7904px;}
+>>>>>>> 0e0edb85a79343e4d020ff05378179e2323b21bd
diff --git a/frontend/web/css/site.css b/frontend/web/css/site.css
new file mode 100644
index 0000000..cf7a38a
--- /dev/null
+++ b/frontend/web/css/site.css
@@ -0,0 +1,96 @@
+html,
+body {
+ height: 100%;
+}
+
+.wrap {
+ min-height: 100%;
+ height: auto;
+ margin: 0 auto -60px;
+ padding: 0 0 60px;
+}
+
+.wrap > .container {
+ padding: 70px 15px 20px;
+}
+
+.footer {
+ height: 60px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #ddd;
+ padding-top: 20px;
+}
+
+.jumbotron {
+ text-align: center;
+ background-color: transparent;
+}
+
+.jumbotron .btn {
+ font-size: 21px;
+ padding: 14px 24px;
+}
+
+.not-set {
+ color: #c55;
+ font-style: italic;
+}
+
+/* add sorting icons to gridview sort links */
+a.asc:after, a.desc:after {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ padding-left: 5px;
+}
+
+a.asc:after {
+ content: /*"\e113"*/ "\e151";
+}
+
+a.desc:after {
+ content: /*"\e114"*/ "\e152";
+}
+
+.sort-numerical a.asc:after {
+ content: "\e153";
+}
+
+.sort-numerical a.desc:after {
+ content: "\e154";
+}
+
+.sort-ordinal a.asc:after {
+ content: "\e155";
+}
+
+.sort-ordinal a.desc:after {
+ content: "\e156";
+}
+
+.grid-view th {
+ white-space: nowrap;
+}
+
+.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;
+}
+.remove-lang {
+ position: absolute;
+ right: 0;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/frontend/web/favicon.ico b/frontend/web/favicon.ico
new file mode 100644
index 0000000..580ed73
Binary files /dev/null and b/frontend/web/favicon.ico differ
diff --git a/frontend/web/images/flags32.png b/frontend/web/images/flags32.png
new file mode 100644
index 0000000..72e9535
Binary files /dev/null and b/frontend/web/images/flags32.png differ
diff --git a/frontend/web/js/option.js b/frontend/web/js/option.js
new file mode 100644
index 0000000..1520807
--- /dev/null
+++ b/frontend/web/js/option.js
@@ -0,0 +1,134 @@
+function readURL(input) {
+ $(input).parents('.tab-pane').find('.image_inputs_prev').remove();
+ var urls = [];
+ if (input.files) {
+ $.each(input.files, function(key, value) {
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ $(input).parent().append(' ');
+ }
+ reader.readAsDataURL(value);
+ });
+ }
+ return urls;
+}
+function checkboxerInit() {
+ $.each($('.checkboxer input[type=radio]:checked'), function(index, value) {
+ console.log(value);
+ $(value).trigger('change');
+ });
+}
+$(function() {
+ var counter = 0;
+ $(document).on('click', '.add_row', function() {
+ counter++;
+ var clone = $('#main_row').clone().html().replace(new RegExp("Option\\[0\\]", 'g'), "Option["+counter+"]");
+ console.log(form);
+ $(clone).appendTo('#'+form);
+ $('#'+form+' button[type=submit]').parent().appendTo('#'+form);
+ });
+ $(document).on('click', '.add_lang', function() {
+ var field_block = $(this).parent().parent();
+ if($(this).hasClass('active')) {
+ $(field_block).find('.main_input').attr('required', '').show();
+ $(field_block).find('.lang_inputs').hide();
+ $(this).removeClass('active');
+ } else {
+ $(field_block).find('.main_input').removeAttr('required').hide();
+ $(field_block).find('.lang_inputs').show();
+ $(this).addClass('active');
+ }
+ });
+ $(document).on('click', '.remove_lang', function() {
+ $(this).parents('.form-wrapper').remove();
+ });
+ $(document).on('change', '.image_inputs_field', function() {
+ readURL(this);
+ });
+ $('a.remove_image').on('click', function(e) {
+ var el = $(this);
+ e.preventDefault();
+ if(confirm(confirm_message)) {
+ $.ajax({
+ type: 'post',
+ url: $(this).attr('href'),
+ data: $(this).data('params')
+ }).done(function() {
+ $(el).parents('.additional_image_container').remove();
+ });
+ }
+ return false;
+ });
+ $.each($('.nav-tabs.f32'), function(key, value) {
+ if($(value).find('li').length > 1) {
+ $(value).find('li').append(' ');
+ }
+ });
+ $(document).on('click', '.dropdown-menu.f32:not(.old) li a[data-lang]', function() {
+ var lang = $(this).data('lang');
+ var flag = $(this).find('span').first().clone();
+ var el = $(this);
+ var id = $(this).attr('href').substr(1);
+ var path = form[id].handler;
+ var view = form[id].view;
+ var model = form[id].model;
+ $.get(path, { language_id: lang, widget_id: id, ajaxView: view, model: model }, function(data) {
+ $('#'+id+'-tabs li').removeClass('active');
+ $('#'+id+'-tabs').append(''+$('').append($(flag)).html()+'
');
+ $('#tab-content-'+id+' .tab-pane.active').removeClass('active');
+ $('#tab-content-'+id).append($(data).find('.ajax-loaded').first());
+ $('body').append($(data).filter('script'));
+ $(el).parent().remove();
+ if(!$('#lang-'+id+' li').length) {
+ $('#'+id+'Lang').addClass('disabled');
+ }
+ if($('#'+id+'-tabs li').length > 1) {
+ $('#'+id+'-tabs li').append(' ')
+ }
+ });
+ });
+ $(document).on('click', '.dropdown-menu.f32.old li a[data-lang]', function() {
+ var lang = $(this).data('lang');
+ var flag = $(this).find('span').first().clone();
+ var el = $(this);
+ var id = $(this).attr('href').substr(1);
+ $.get(form[id], { language_id: lang, widget_id: id }, function(data) {
+ $('#'+id+'-tabs li').removeClass('active');
+ $('#'+id+'-tabs').append(''+$('').append($(flag)).html()+'
');
+ $('#tab-content-'+id+' .tab-pane.active').removeClass('active');
+ $('#tab-content-'+id).append($(data).find('.ajax-loaded').first());
+ $('body').append($(data).filter('script'));
+ $(el).parent().remove();
+ if(!$('#lang-'+id+' li').length) {
+ $('#'+id+'Lang').addClass('disabled');
+ }
+ if($('#'+id+'-tabs li').length > 1) {
+ $('#'+id+'-tabs li').append(' ')
+ }
+ });
+ });
+ $(document).on('click', '.remove-lang', function() {
+ var lang = $(this).parent().data('lang');
+ var flag = $(this).parent().find('span.flag').first().clone();
+ var id = $(this).parent().find('a[aria-controls]').first().attr('aria-controls').substr(0,8);
+ $('#'+id+'-'+lang).remove();
+ $('#lang-'+id).append(''+$('').append($(flag)).html()+'
');
+ $('#'+id+'Lang').removeClass('disabled');
+ $(this).parent().remove();
+ if($('#'+id+'-tabs li').length <= 1) {
+ $('#'+id+'-tabs li').find('.remove-lang').remove();
+ }
+ if(!$('#'+id+'-tabs>li.active').length) {
+ $('#'+id+'-tabs>li').first().find('a').tab('show');
+ }
+ });
+ $(document).on('change', '.checkboxer .checkboxer_label input[type=radio]', function() {
+ console.log($(this).val());
+ $(this).parents('.checkboxer').find('.checkboxer_container').removeClass('active');
+ $(this).parents('.checkboxer_container').addClass('active');
+ });
+ $.each($('.f32'), function(i, val) {
+ $(val).find('a[role=tab]').first().trigger('click');
+ });
+ checkboxerInit();
+});
\ No newline at end of file
diff --git a/frontend/web/robots.txt b/frontend/web/robots.txt
new file mode 100644
index 0000000..6f27bb6
--- /dev/null
+++ b/frontend/web/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:
\ No newline at end of file
diff --git a/init b/init
new file mode 100644
index 0000000..1b35927
--- /dev/null
+++ b/init
@@ -0,0 +1,209 @@
+#!/usr/bin/env php
+
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+if (!extension_loaded('openssl')) {
+ die('The OpenSSL PHP extension is required by Yii2.');
+}
+
+$params = getParams();
+$root = str_replace('\\', '/', __DIR__);
+$envs = require("$root/environments/index.php");
+$envNames = array_keys($envs);
+
+echo "Yii Application Initialization Tool v1.0\n\n";
+
+$envName = null;
+if (empty($params['env']) || $params['env'] === '1') {
+ echo "Which environment do you want the application to be initialized in?\n\n";
+ foreach ($envNames as $i => $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)) {
+ $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);
+ }
+}
+
+echo "\n Start initialization ...\n\n";
+$files = getFileList("$root/environments/{$env['path']}");
+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;
+ }
+}
+
+$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 getParams()
+{
+ $rawParams = [];
+ if (isset($_SERVER['argv'])) {
+ $rawParams = $_SERVER['argv'];
+ array_shift($rawParams);
+ }
+
+ $params = [];
+ foreach ($rawParams as $param) {
+ if (preg_match('/^--(\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) {
+ echo " chmod 0777 $writable\n";
+ @chmod("$root/$writable", 0777);
+ }
+}
+
+function setExecutable($root, $paths)
+{
+ foreach ($paths as $executable) {
+ echo " chmod 0755 $executable\n";
+ @chmod("$root/$executable", 0755);
+ }
+}
+
+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) {
+ echo " symlink " . $root . "/" . $target . " " . $root . "/" . $link . "\n";
+ //first removing folders to avoid errors if the folder already exists
+ @rmdir($root . "/" . $link);
+ @symlink($root . "/" . $target, $root . "/" . $link);
+ }
+}
diff --git a/init.bat b/init.bat
new file mode 100644
index 0000000..e50c242
--- /dev/null
+++ b/init.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem Yii command line init script for Windows.
+rem
+rem @author Qiang Xue
+rem @link http://www.yiiframework.com/
+rem @copyright Copyright (c) 2008 Yii Software LLC
+rem @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%init" %*
+
+@endlocal
diff --git a/rbac/assignments.php b/rbac/assignments.php
new file mode 100644
index 0000000..34bfa1d
--- /dev/null
+++ b/rbac/assignments.php
@@ -0,0 +1,7 @@
+Error';
+ echo 'The path to yii framework seems to be incorrect.
';
+ echo 'You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . ' .
';
+ echo 'Please refer to the README on how to install Yii.
';
+}
+
+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',
+ ),
+);
+$requirementsChecker->checkYii()->check($requirements)->render();
diff --git a/test.html b/test.html
new file mode 100644
index 0000000..566549b
--- /dev/null
+++ b/test.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Title
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..ad7f016
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,58 @@
+This directory contains various tests for the advanced applications.
+
+Tests in `codeception` directory are developed with [Codeception PHP Testing Framework](http://codeception.com/).
+
+After creating and setting up the advanced application, follow these steps to prepare for the tests:
+
+1. Install Codeception if it's not yet installed:
+
+ ```
+ composer global require "codeception/codeception=2.0.*" "codeception/specify=*" "codeception/verify=*"
+ ```
+
+ If you've never used Composer for global packages run `composer global status`. It should output:
+
+ ```
+ Changed current directory to
+ ```
+
+ Then add `/vendor/bin` to you `PATH` environment variable. Now you're able to use `codecept` from command
+ line globally.
+
+2. Install faker extension by running the following from template root directory where `composer.json` is:
+
+ ```
+ composer require --dev yiisoft/yii2-faker:*
+ ```
+
+3. Create `yii2_advanced_tests` database then update it by applying migrations:
+
+ ```
+ codeception/bin/yii migrate
+ ```
+
+4. In order to be able to run acceptance tests you need to start a webserver. The simplest way is to use PHP built in
+ webserver. In the root directory where `common`, `frontend` etc. are execute the following:
+
+ ```
+ php -S localhost:8080
+ ```
+
+5. Now you can run the tests with the following commands, assuming you are in the `tests/codeception` directory:
+
+ ```
+ # frontend tests
+ cd frontend
+ codecept build
+ codecept run
+
+ # backend tests
+
+ cd backend
+ codecept build
+ codecept run
+
+ # etc.
+ ```
+
+ If you already have run `codecept build` for each application, you can skip that step and run all tests by a single `codecept run`.
diff --git a/tests/codeception.yml b/tests/codeception.yml
new file mode 100644
index 0000000..1a793ed
--- /dev/null
+++ b/tests/codeception.yml
@@ -0,0 +1,11 @@
+include:
+ - codeception/common
+ - codeception/console
+ - codeception/backend
+ - codeception/frontend
+
+paths:
+ log: codeception/_output
+
+settings:
+ colors: true
diff --git a/tests/codeception/_output/.gitignore b/tests/codeception/_output/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/tests/codeception/_output/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/tests/codeception/backend/.gitignore b/tests/codeception/backend/.gitignore
new file mode 100644
index 0000000..985dbb4
--- /dev/null
+++ b/tests/codeception/backend/.gitignore
@@ -0,0 +1,4 @@
+# these files are auto generated by codeception build
+/unit/UnitTester.php
+/functional/FunctionalTester.php
+/acceptance/AcceptanceTester.php
diff --git a/tests/codeception/backend/_bootstrap.php b/tests/codeception/backend/_bootstrap.php
new file mode 100644
index 0000000..a28a3d2
--- /dev/null
+++ b/tests/codeception/backend/_bootstrap.php
@@ -0,0 +1,23 @@
+wantTo('ensure login page works');
+
+$loginPage = LoginPage::openBy($I);
+
+$I->amGoingTo('submit login form with no data');
+$loginPage->login('', '');
+if (method_exists($I, 'wait')) {
+ $I->wait(3); // only for selenium
+}
+$I->expectTo('see validations errors');
+$I->see('Username cannot be blank.', '.help-block');
+$I->see('Password cannot be blank.', '.help-block');
+
+$I->amGoingTo('try to login with wrong credentials');
+$I->expectTo('see validations errors');
+$loginPage->login('admin', 'wrong');
+if (method_exists($I, 'wait')) {
+ $I->wait(3); // only for selenium
+}
+$I->expectTo('see validations errors');
+$I->see('Incorrect username or password.', '.help-block');
+
+$I->amGoingTo('try to login with correct credentials');
+$loginPage->login('erau', 'password_0');
+if (method_exists($I, 'wait')) {
+ $I->wait(3); // only for selenium
+}
+$I->expectTo('see that user is logged');
+$I->seeLink('Logout (erau)');
+$I->dontSeeLink('Login');
+$I->dontSeeLink('Signup');
+/** Uncomment if using WebDriver
+ * $I->click('Logout (erau)');
+ * $I->dontSeeLink('Logout (erau)');
+ * $I->seeLink('Login');
+ */
diff --git a/tests/codeception/backend/acceptance/_bootstrap.php b/tests/codeception/backend/acceptance/_bootstrap.php
new file mode 100644
index 0000000..411855e
--- /dev/null
+++ b/tests/codeception/backend/acceptance/_bootstrap.php
@@ -0,0 +1,2 @@
+wantTo('ensure login page works');
+
+$loginPage = LoginPage::openBy($I);
+
+$I->amGoingTo('submit login form with no data');
+$loginPage->login('', '');
+$I->expectTo('see validations errors');
+$I->see('Username cannot be blank.', '.help-block');
+$I->see('Password cannot be blank.', '.help-block');
+
+$I->amGoingTo('try to login with wrong credentials');
+$I->expectTo('see validations errors');
+$loginPage->login('admin', 'wrong');
+$I->expectTo('see validations errors');
+$I->see('Incorrect username or password.', '.help-block');
+
+$I->amGoingTo('try to login with correct credentials');
+$loginPage->login('erau', 'password_0');
+$I->expectTo('see that user is logged');
+$I->seeLink('Logout (erau)');
+$I->dontSeeLink('Login');
+$I->dontSeeLink('Signup');
diff --git a/tests/codeception/backend/functional/_bootstrap.php b/tests/codeception/backend/functional/_bootstrap.php
new file mode 100644
index 0000000..94f3fbd
--- /dev/null
+++ b/tests/codeception/backend/functional/_bootstrap.php
@@ -0,0 +1,2 @@
+run();
+exit($exitCode);
diff --git a/tests/codeception/bin/yii.bat b/tests/codeception/bin/yii.bat
new file mode 100644
index 0000000..d516b3a
--- /dev/null
+++ b/tests/codeception/bin/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem Yii command line bootstrap script for Windows.
+rem
+rem @author Qiang Xue
+rem @link http://www.yiiframework.com/
+rem @copyright Copyright (c) 2008 Yii Software LLC
+rem @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/tests/codeception/common/.gitignore b/tests/codeception/common/.gitignore
new file mode 100644
index 0000000..985dbb4
--- /dev/null
+++ b/tests/codeception/common/.gitignore
@@ -0,0 +1,4 @@
+# these files are auto generated by codeception build
+/unit/UnitTester.php
+/functional/FunctionalTester.php
+/acceptance/AcceptanceTester.php
diff --git a/tests/codeception/common/_bootstrap.php b/tests/codeception/common/_bootstrap.php
new file mode 100644
index 0000000..cea3ee5
--- /dev/null
+++ b/tests/codeception/common/_bootstrap.php
@@ -0,0 +1,15 @@
+actor->fillField('input[name="LoginForm[username]"]', $username);
+ $this->actor->fillField('input[name="LoginForm[password]"]', $password);
+ $this->actor->click('login-button');
+ }
+}
diff --git a/tests/codeception/common/_support/FixtureHelper.php b/tests/codeception/common/_support/FixtureHelper.php
new file mode 100644
index 0000000..c5ebcf1
--- /dev/null
+++ b/tests/codeception/common/_support/FixtureHelper.php
@@ -0,0 +1,72 @@
+loadFixtures();
+ }
+
+ /**
+ * Method is called after all suite tests run
+ */
+ public function _afterSuite()
+ {
+ $this->unloadFixtures();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function globalFixtures()
+ {
+ return [
+ InitDbFixture::className(),
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function fixtures()
+ {
+ return [
+ 'user' => [
+ 'class' => UserFixture::className(),
+ 'dataFile' => '@tests/codeception/common/fixtures/data/init_login.php',
+ ],
+ ];
+ }
+}
diff --git a/tests/codeception/common/codeception.yml b/tests/codeception/common/codeception.yml
new file mode 100644
index 0000000..e8a3407
--- /dev/null
+++ b/tests/codeception/common/codeception.yml
@@ -0,0 +1,13 @@
+namespace: tests\codeception\common
+actor: Tester
+paths:
+ tests: .
+ log: _output
+ data: _data
+ helpers: _support
+settings:
+ bootstrap: _bootstrap.php
+ suite_class: \PHPUnit_Framework_TestSuite
+ colors: true
+ memory_limit: 1024M
+ log: true
diff --git a/tests/codeception/common/fixtures/UserFixture.php b/tests/codeception/common/fixtures/UserFixture.php
new file mode 100644
index 0000000..7153c8c
--- /dev/null
+++ b/tests/codeception/common/fixtures/UserFixture.php
@@ -0,0 +1,13 @@
+ '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/tests/codeception/common/templates/fixtures/user.php b/tests/codeception/common/templates/fixtures/user.php
new file mode 100644
index 0000000..d3f83b5
--- /dev/null
+++ b/tests/codeception/common/templates/fixtures/user.php
@@ -0,0 +1,17 @@
+getSecurity();
+
+return [
+ 'username' => $faker->userName,
+ 'email' => $faker->email,
+ 'auth_key' => $security->generateRandomString(),
+ 'password_hash' => $security->generatePasswordHash('password_' . $index),
+ 'password_reset_token' => $security->generateRandomString() . '_' . time(),
+ 'created_at' => time(),
+ 'updated_at' => time(),
+];
diff --git a/tests/codeception/common/unit.suite.yml b/tests/codeception/common/unit.suite.yml
new file mode 100644
index 0000000..a0582a5
--- /dev/null
+++ b/tests/codeception/common/unit.suite.yml
@@ -0,0 +1,6 @@
+# Codeception Test Suite Configuration
+
+# suite for unit (internal) tests.
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+
+class_name: UnitTester
diff --git a/tests/codeception/common/unit/DbTestCase.php b/tests/codeception/common/unit/DbTestCase.php
new file mode 100644
index 0000000..2159a69
--- /dev/null
+++ b/tests/codeception/common/unit/DbTestCase.php
@@ -0,0 +1,11 @@
+ '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/tests/codeception/common/unit/models/LoginFormTest.php b/tests/codeception/common/unit/models/LoginFormTest.php
new file mode 100644
index 0000000..54c8209
--- /dev/null
+++ b/tests/codeception/common/unit/models/LoginFormTest.php
@@ -0,0 +1,93 @@
+ [
+ 'user' => [
+ 'class' => 'yii\web\User',
+ 'identityClass' => 'common\models\User',
+ ],
+ ],
+ ]);
+ }
+
+ protected function tearDown()
+ {
+ Yii::$app->user->logout();
+ parent::tearDown();
+ }
+
+ public function testLoginNoUser()
+ {
+ $model = new LoginForm([
+ 'username' => 'not_existing_username',
+ 'password' => 'not_existing_password',
+ ]);
+
+ $this->specify('user should not be able to login, when there is no identity', function () use ($model) {
+ expect('model should not login user', $model->login())->false();
+ expect('user should not be logged in', Yii::$app->user->isGuest)->true();
+ });
+ }
+
+ public function testLoginWrongPassword()
+ {
+ $model = new LoginForm([
+ 'username' => 'bayer.hudson',
+ 'password' => 'wrong_password',
+ ]);
+
+ $this->specify('user should not be able to login with wrong password', function () use ($model) {
+ expect('model should not login user', $model->login())->false();
+ expect('error message should be set', $model->errors)->hasKey('password');
+ expect('user should not be logged in', Yii::$app->user->isGuest)->true();
+ });
+ }
+
+ public function testLoginCorrect()
+ {
+
+ $model = new LoginForm([
+ 'username' => 'bayer.hudson',
+ 'password' => 'password_0',
+ ]);
+
+ $this->specify('user should be able to login with correct credentials', function () use ($model) {
+ expect('model should login user', $model->login())->true();
+ expect('error message should not be set', $model->errors)->hasntKey('password');
+ expect('user should be logged in', Yii::$app->user->isGuest)->false();
+ });
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function fixtures()
+ {
+ return [
+ 'user' => [
+ 'class' => UserFixture::className(),
+ 'dataFile' => '@tests/codeception/common/unit/fixtures/data/models/user.php'
+ ],
+ ];
+ }
+}
diff --git a/tests/codeception/config/acceptance.php b/tests/codeception/config/acceptance.php
new file mode 100644
index 0000000..9318da5
--- /dev/null
+++ b/tests/codeception/config/acceptance.php
@@ -0,0 +1,7 @@
+ 'app-common',
+ 'basePath' => dirname(__DIR__),
+ ]
+);
diff --git a/tests/codeception/config/config.php b/tests/codeception/config/config.php
new file mode 100644
index 0000000..b478679
--- /dev/null
+++ b/tests/codeception/config/config.php
@@ -0,0 +1,26 @@
+ 'en-US',
+ 'controllerMap' => [
+ 'fixture' => [
+ 'class' => 'yii\faker\FixtureController',
+ 'fixtureDataPath' => '@tests/codeception/common/fixtures/data',
+ 'templatePath' => '@tests/codeception/common/templates/fixtures',
+ 'namespace' => 'tests\codeception\common\fixtures',
+ ],
+ ],
+ 'components' => [
+ 'db' => [
+ 'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_tests',
+ ],
+ 'mailer' => [
+ 'useFileTransport' => true,
+ ],
+ 'urlManager' => [
+ 'showScriptName' => true,
+ ],
+ ],
+];
diff --git a/tests/codeception/config/console/unit.php b/tests/codeception/config/console/unit.php
new file mode 100644
index 0000000..4d3aeb0
--- /dev/null
+++ b/tests/codeception/config/console/unit.php
@@ -0,0 +1,14 @@
+ [
+ 'request' => [
+ // it's not recommended to run functional tests with CSRF validation enabled
+ 'enableCsrfValidation' => false,
+ // but if you absolutely need it set cookie domain to localhost
+ /*
+ 'csrfCookie' => [
+ 'domain' => 'localhost',
+ ],
+ */
+ ],
+ ],
+];
\ No newline at end of file
diff --git a/tests/codeception/config/unit.php b/tests/codeception/config/unit.php
new file mode 100644
index 0000000..6bd08d3
--- /dev/null
+++ b/tests/codeception/config/unit.php
@@ -0,0 +1,7 @@
+ $value) {
+ $inputType = $field === 'body' ? 'textarea' : 'input';
+ $this->actor->fillField($inputType . '[name="ContactForm[' . $field . ']"]', $value);
+ }
+ $this->actor->click('contact-button');
+ }
+}
diff --git a/tests/codeception/frontend/_pages/SignupPage.php b/tests/codeception/frontend/_pages/SignupPage.php
new file mode 100644
index 0000000..0e1cefa
--- /dev/null
+++ b/tests/codeception/frontend/_pages/SignupPage.php
@@ -0,0 +1,27 @@
+ $value) {
+ $inputType = $field === 'body' ? 'textarea' : 'input';
+ $this->actor->fillField($inputType . '[name="SignupForm[' . $field . ']"]', $value);
+ }
+ $this->actor->click('signup-button');
+ }
+}
diff --git a/tests/codeception/frontend/acceptance.suite.yml b/tests/codeception/frontend/acceptance.suite.yml
new file mode 100644
index 0000000..1828a04
--- /dev/null
+++ b/tests/codeception/frontend/acceptance.suite.yml
@@ -0,0 +1,28 @@
+# Codeception Test Suite Configuration
+
+# suite for acceptance tests.
+# perform tests in browser using the Selenium-like tools.
+# powered by Mink (http://mink.behat.org).
+# (tip: that's what your customer will see).
+# (tip: test your ajax and javascript by one of Mink drivers).
+
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+
+class_name: AcceptanceTester
+modules:
+ enabled:
+ - PhpBrowser
+ - tests\codeception\common\_support\FixtureHelper
+# you can use WebDriver instead of PhpBrowser to test javascript and ajax.
+# This will require you to install selenium. See http://codeception.com/docs/04-AcceptanceTests#Selenium
+# "restart" option is used by the WebDriver to start each time per test-file new session and cookies,
+# it is useful if you want to login in your app in each test.
+# - WebDriver
+ config:
+ PhpBrowser:
+# PLEASE ADJUST IT TO THE ACTUAL ENTRY POINT WITHOUT PATH INFO
+ url: http://localhost:8080
+# WebDriver:
+# url: http://localhost:8080
+# browser: firefox
+# restart: true
diff --git a/tests/codeception/frontend/acceptance/AboutCept.php b/tests/codeception/frontend/acceptance/AboutCept.php
new file mode 100644
index 0000000..50bf9ba
--- /dev/null
+++ b/tests/codeception/frontend/acceptance/AboutCept.php
@@ -0,0 +1,10 @@
+wantTo('ensure that about works');
+AboutPage::openBy($I);
+$I->see('About', 'h1');
diff --git a/tests/codeception/frontend/acceptance/ContactCept.php b/tests/codeception/frontend/acceptance/ContactCept.php
new file mode 100644
index 0000000..f7a6bc8
--- /dev/null
+++ b/tests/codeception/frontend/acceptance/ContactCept.php
@@ -0,0 +1,56 @@
+wantTo('ensure that contact works');
+
+$contactPage = ContactPage::openBy($I);
+
+$I->see('Contact', 'h1');
+
+$I->amGoingTo('submit contact form with no data');
+$contactPage->submit([]);
+if (method_exists($I, 'wait')) {
+ $I->wait(3); // only for selenium
+}
+$I->expectTo('see validations errors');
+$I->see('Contact', 'h1');
+$I->see('Name cannot be blank', '.help-block');
+$I->see('Email cannot be blank', '.help-block');
+$I->see('Subject cannot be blank', '.help-block');
+$I->see('Body cannot be blank', '.help-block');
+$I->see('The verification code is incorrect', '.help-block');
+
+$I->amGoingTo('submit contact form with not correct email');
+$contactPage->submit([
+ 'name' => 'tester',
+ 'email' => 'tester.email',
+ 'subject' => 'test subject',
+ 'body' => 'test content',
+ 'verifyCode' => 'testme',
+]);
+if (method_exists($I, 'wait')) {
+ $I->wait(3); // only for selenium
+}
+$I->expectTo('see that email adress is wrong');
+$I->dontSee('Name cannot be blank', '.help-block');
+$I->see('Email is not a valid email address.', '.help-block');
+$I->dontSee('Subject cannot be blank', '.help-block');
+$I->dontSee('Body cannot be blank', '.help-block');
+$I->dontSee('The verification code is incorrect', '.help-block');
+
+$I->amGoingTo('submit contact form with correct data');
+$contactPage->submit([
+ 'name' => 'tester',
+ 'email' => 'tester@example.com',
+ 'subject' => 'test subject',
+ 'body' => 'test content',
+ 'verifyCode' => 'testme',
+]);
+if (method_exists($I, 'wait')) {
+ $I->wait(3); // only for selenium
+}
+$I->see('Thank you for contacting us. We will respond to you as soon as possible.');
diff --git a/tests/codeception/frontend/acceptance/HomeCept.php b/tests/codeception/frontend/acceptance/HomeCept.php
new file mode 100644
index 0000000..9566a2e
--- /dev/null
+++ b/tests/codeception/frontend/acceptance/HomeCept.php
@@ -0,0 +1,12 @@
+wantTo('ensure that home page works');
+$I->amOnPage(Yii::$app->homeUrl);
+$I->see('My Company');
+$I->seeLink('About');
+$I->click('About');
+$I->see('This is the About page.');
diff --git a/tests/codeception/frontend/acceptance/LoginCept.php b/tests/codeception/frontend/acceptance/LoginCept.php
new file mode 100644
index 0000000..599e24f
--- /dev/null
+++ b/tests/codeception/frontend/acceptance/LoginCept.php
@@ -0,0 +1,34 @@
+wantTo('ensure login page works');
+
+$loginPage = LoginPage::openBy($I);
+
+$I->amGoingTo('submit login form with no data');
+$loginPage->login('', '');
+$I->expectTo('see validations errors');
+$I->see('Username cannot be blank.', '.help-block');
+$I->see('Password cannot be blank.', '.help-block');
+
+$I->amGoingTo('try to login with wrong credentials');
+$I->expectTo('see validations errors');
+$loginPage->login('admin', 'wrong');
+$I->expectTo('see validations errors');
+$I->see('Incorrect username or password.', '.help-block');
+
+$I->amGoingTo('try to login with correct credentials');
+$loginPage->login('erau', 'password_0');
+$I->expectTo('see that user is logged');
+$I->seeLink('Logout (erau)');
+$I->dontSeeLink('Login');
+$I->dontSeeLink('Signup');
+/** Uncomment if using WebDriver
+ * $I->click('Logout (erau)');
+ * $I->dontSeeLink('Logout (erau)');
+ * $I->seeLink('Login');
+ */
diff --git a/tests/codeception/frontend/acceptance/SignupCest.php b/tests/codeception/frontend/acceptance/SignupCest.php
new file mode 100644
index 0000000..ab4b7bb
--- /dev/null
+++ b/tests/codeception/frontend/acceptance/SignupCest.php
@@ -0,0 +1,82 @@
+ 'tester.email@example.com',
+ 'username' => 'tester',
+ ]);
+ }
+
+ /**
+ * This method is called when test fails.
+ * @param \Codeception\Event\FailEvent $event
+ */
+ public function _fail($event)
+ {
+ }
+
+ /**
+ * @param \codeception_frontend\AcceptanceTester $I
+ * @param \Codeception\Scenario $scenario
+ */
+ public function testUserSignup($I, $scenario)
+ {
+ $I->wantTo('ensure that signup works');
+
+ $signupPage = SignupPage::openBy($I);
+ $I->see('Signup', 'h1');
+ $I->see('Please fill out the following fields to signup:');
+
+ $I->amGoingTo('submit signup form with no data');
+
+ $signupPage->submit([]);
+
+ $I->expectTo('see validation errors');
+ $I->see('Username cannot be blank.', '.help-block');
+ $I->see('Email cannot be blank.', '.help-block');
+ $I->see('Password cannot be blank.', '.help-block');
+
+ $I->amGoingTo('submit signup form with not correct email');
+ $signupPage->submit([
+ 'username' => 'tester',
+ 'email' => 'tester.email',
+ 'password' => 'tester_password',
+ ]);
+
+ $I->expectTo('see that email address is wrong');
+ $I->dontSee('Username cannot be blank.', '.help-block');
+ $I->dontSee('Password cannot be blank.', '.help-block');
+ $I->see('Email is not a valid email address.', '.help-block');
+
+ $I->amGoingTo('submit signup form with correct email');
+ $signupPage->submit([
+ 'username' => 'tester',
+ 'email' => 'tester.email@example.com',
+ 'password' => 'tester_password',
+ ]);
+
+ $I->expectTo('see that user logged in');
+ $I->seeLink('Logout (tester)');
+ }
+}
diff --git a/tests/codeception/frontend/acceptance/_bootstrap.php b/tests/codeception/frontend/acceptance/_bootstrap.php
new file mode 100644
index 0000000..b0a40ef
--- /dev/null
+++ b/tests/codeception/frontend/acceptance/_bootstrap.php
@@ -0,0 +1,2 @@
+wantTo('ensure that about works');
+AboutPage::openBy($I);
+$I->see('About', 'h1');
diff --git a/tests/codeception/frontend/functional/ContactCept.php b/tests/codeception/frontend/functional/ContactCept.php
new file mode 100644
index 0000000..c61d4c2
--- /dev/null
+++ b/tests/codeception/frontend/functional/ContactCept.php
@@ -0,0 +1,47 @@
+wantTo('ensure that contact works');
+
+$contactPage = ContactPage::openBy($I);
+
+$I->see('Contact', 'h1');
+
+$I->amGoingTo('submit contact form with no data');
+$contactPage->submit([]);
+$I->expectTo('see validations errors');
+$I->see('Contact', 'h1');
+$I->see('Name cannot be blank', '.help-block');
+$I->see('Email cannot be blank', '.help-block');
+$I->see('Subject cannot be blank', '.help-block');
+$I->see('Body cannot be blank', '.help-block');
+$I->see('The verification code is incorrect', '.help-block');
+
+$I->amGoingTo('submit contact form with not correct email');
+$contactPage->submit([
+ 'name' => 'tester',
+ 'email' => 'tester.email',
+ 'subject' => 'test subject',
+ 'body' => 'test content',
+ 'verifyCode' => 'testme',
+]);
+$I->expectTo('see that email adress is wrong');
+$I->dontSee('Name cannot be blank', '.help-block');
+$I->see('Email is not a valid email address.', '.help-block');
+$I->dontSee('Subject cannot be blank', '.help-block');
+$I->dontSee('Body cannot be blank', '.help-block');
+$I->dontSee('The verification code is incorrect', '.help-block');
+
+$I->amGoingTo('submit contact form with correct data');
+$contactPage->submit([
+ 'name' => 'tester',
+ 'email' => 'tester@example.com',
+ 'subject' => 'test subject',
+ 'body' => 'test content',
+ 'verifyCode' => 'testme',
+]);
+$I->see('Thank you for contacting us. We will respond to you as soon as possible.');
diff --git a/tests/codeception/frontend/functional/HomeCept.php b/tests/codeception/frontend/functional/HomeCept.php
new file mode 100644
index 0000000..f340061
--- /dev/null
+++ b/tests/codeception/frontend/functional/HomeCept.php
@@ -0,0 +1,12 @@
+wantTo('ensure that home page works');
+$I->amOnPage(Yii::$app->homeUrl);
+$I->see('My Company');
+$I->seeLink('About');
+$I->click('About');
+$I->see('This is the About page.');
diff --git a/tests/codeception/frontend/functional/LoginCept.php b/tests/codeception/frontend/functional/LoginCept.php
new file mode 100644
index 0000000..daca12c
--- /dev/null
+++ b/tests/codeception/frontend/functional/LoginCept.php
@@ -0,0 +1,29 @@
+wantTo('ensure login page works');
+
+$loginPage = LoginPage::openBy($I);
+
+$I->amGoingTo('submit login form with no data');
+$loginPage->login('', '');
+$I->expectTo('see validations errors');
+$I->see('Username cannot be blank.', '.help-block');
+$I->see('Password cannot be blank.', '.help-block');
+
+$I->amGoingTo('try to login with wrong credentials');
+$I->expectTo('see validations errors');
+$loginPage->login('admin', 'wrong');
+$I->expectTo('see validations errors');
+$I->see('Incorrect username or password.', '.help-block');
+
+$I->amGoingTo('try to login with correct credentials');
+$loginPage->login('erau', 'password_0');
+$I->expectTo('see that user is logged');
+$I->seeLink('Logout (erau)');
+$I->dontSeeLink('Login');
+$I->dontSeeLink('Signup');
diff --git a/tests/codeception/frontend/functional/SignupCest.php b/tests/codeception/frontend/functional/SignupCest.php
new file mode 100644
index 0000000..525b037
--- /dev/null
+++ b/tests/codeception/frontend/functional/SignupCest.php
@@ -0,0 +1,90 @@
+ 'tester.email@example.com',
+ 'username' => 'tester',
+ ]);
+ }
+
+ /**
+ * This method is called when test fails.
+ * @param \Codeception\Event\FailEvent $event
+ */
+ public function _fail($event)
+ {
+
+ }
+
+ /**
+ *
+ * @param \codeception_frontend\FunctionalTester $I
+ * @param \Codeception\Scenario $scenario
+ */
+ public function testUserSignup($I, $scenario)
+ {
+ $I->wantTo('ensure that signup works');
+
+ $signupPage = SignupPage::openBy($I);
+ $I->see('Signup', 'h1');
+ $I->see('Please fill out the following fields to signup:');
+
+ $I->amGoingTo('submit signup form with no data');
+
+ $signupPage->submit([]);
+
+ $I->expectTo('see validation errors');
+ $I->see('Username cannot be blank.', '.help-block');
+ $I->see('Email cannot be blank.', '.help-block');
+ $I->see('Password cannot be blank.', '.help-block');
+
+ $I->amGoingTo('submit signup form with not correct email');
+ $signupPage->submit([
+ 'username' => 'tester',
+ 'email' => 'tester.email',
+ 'password' => 'tester_password',
+ ]);
+
+ $I->expectTo('see that email address is wrong');
+ $I->dontSee('Username cannot be blank.', '.help-block');
+ $I->dontSee('Password cannot be blank.', '.help-block');
+ $I->see('Email is not a valid email address.', '.help-block');
+
+ $I->amGoingTo('submit signup form with correct email');
+ $signupPage->submit([
+ 'username' => 'tester',
+ 'email' => 'tester.email@example.com',
+ 'password' => 'tester_password',
+ ]);
+
+ $I->expectTo('see that user is created');
+ $I->seeRecord('common\models\User', [
+ 'username' => 'tester',
+ 'email' => 'tester.email@example.com',
+ ]);
+
+ $I->expectTo('see that user logged in');
+ $I->seeLink('Logout (tester)');
+ }
+}
diff --git a/tests/codeception/frontend/functional/_bootstrap.php b/tests/codeception/frontend/functional/_bootstrap.php
new file mode 100644
index 0000000..1abc491
--- /dev/null
+++ b/tests/codeception/frontend/functional/_bootstrap.php
@@ -0,0 +1,3 @@
+ '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',
+ ],
+];
diff --git a/tests/codeception/frontend/unit/models/ContactFormTest.php b/tests/codeception/frontend/unit/models/ContactFormTest.php
new file mode 100644
index 0000000..9aaf595
--- /dev/null
+++ b/tests/codeception/frontend/unit/models/ContactFormTest.php
@@ -0,0 +1,59 @@
+mailer->fileTransportCallback = function ($mailer, $message) {
+ return 'testing_message.eml';
+ };
+ }
+
+ protected function tearDown()
+ {
+ unlink($this->getMessageFile());
+ parent::tearDown();
+ }
+
+ public function testContact()
+ {
+ $model = new ContactForm();
+
+ $model->attributes = [
+ 'name' => 'Tester',
+ 'email' => 'tester@example.com',
+ 'subject' => 'very important letter subject',
+ 'body' => 'body of current message',
+ ];
+
+ $model->sendEmail('admin@example.com');
+
+ $this->specify('email should be send', function () {
+ expect('email file should exist', file_exists($this->getMessageFile()))->true();
+ });
+
+ $this->specify('message should contain correct data', function () use ($model) {
+ $emailMessage = file_get_contents($this->getMessageFile());
+
+ expect('email should contain user name', $emailMessage)->contains($model->name);
+ expect('email should contain sender email', $emailMessage)->contains($model->email);
+ expect('email should contain subject', $emailMessage)->contains($model->subject);
+ expect('email should contain body', $emailMessage)->contains($model->body);
+ });
+ }
+
+ private function getMessageFile()
+ {
+ return Yii::getAlias(Yii::$app->mailer->fileTransportPath) . '/testing_message.eml';
+ }
+}
diff --git a/tests/codeception/frontend/unit/models/PasswordResetRequestFormTest.php b/tests/codeception/frontend/unit/models/PasswordResetRequestFormTest.php
new file mode 100644
index 0000000..ced8cce
--- /dev/null
+++ b/tests/codeception/frontend/unit/models/PasswordResetRequestFormTest.php
@@ -0,0 +1,87 @@
+mailer->fileTransportCallback = function ($mailer, $message) {
+ return 'testing_message.eml';
+ };
+ }
+
+ protected function tearDown()
+ {
+ @unlink($this->getMessageFile());
+
+ parent::tearDown();
+ }
+
+ public function testSendEmailWrongUser()
+ {
+ $this->specify('no user with such email, message should not be sent', function () {
+
+ $model = new PasswordResetRequestForm();
+ $model->email = 'not-existing-email@example.com';
+
+ expect('email not sent', $model->sendEmail())->false();
+
+ });
+
+ $this->specify('user is not active, message should not be sent', function () {
+
+ $model = new PasswordResetRequestForm();
+ $model->email = $this->user[1]['email'];
+
+ expect('email not sent', $model->sendEmail())->false();
+
+ });
+ }
+
+ public function testSendEmailCorrectUser()
+ {
+ $model = new PasswordResetRequestForm();
+ $model->email = $this->user[0]['email'];
+ $user = User::findOne(['password_reset_token' => $this->user[0]['password_reset_token']]);
+
+ expect('email sent', $model->sendEmail())->true();
+ expect('user has valid token', $user->password_reset_token)->notNull();
+
+ $this->specify('message has correct format', function () use ($model) {
+
+ expect('message file exists', file_exists($this->getMessageFile()))->true();
+
+ $message = file_get_contents($this->getMessageFile());
+ expect('message "from" is correct', $message)->contains(Yii::$app->params['supportEmail']);
+ expect('message "to" is correct', $message)->contains($model->email);
+
+ });
+ }
+
+ public function fixtures()
+ {
+ return [
+ 'user' => [
+ 'class' => UserFixture::className(),
+ 'dataFile' => '@tests/codeception/frontend/unit/fixtures/data/models/user.php'
+ ],
+ ];
+ }
+
+ private function getMessageFile()
+ {
+ return Yii::getAlias(Yii::$app->mailer->fileTransportPath) . '/testing_message.eml';
+ }
+}
diff --git a/tests/codeception/frontend/unit/models/ResetPasswordFormTest.php b/tests/codeception/frontend/unit/models/ResetPasswordFormTest.php
new file mode 100644
index 0000000..a4dd021
--- /dev/null
+++ b/tests/codeception/frontend/unit/models/ResetPasswordFormTest.php
@@ -0,0 +1,43 @@
+user[0]['password_reset_token']);
+ expect('password should be resetted', $form->resetPassword())->true();
+ }
+
+ public function fixtures()
+ {
+ return [
+ 'user' => [
+ 'class' => UserFixture::className(),
+ 'dataFile' => '@tests/codeception/frontend/unit/fixtures/data/models/user.php'
+ ],
+ ];
+ }
+}
diff --git a/tests/codeception/frontend/unit/models/SignupFormTest.php b/tests/codeception/frontend/unit/models/SignupFormTest.php
new file mode 100644
index 0000000..4d08e8c
--- /dev/null
+++ b/tests/codeception/frontend/unit/models/SignupFormTest.php
@@ -0,0 +1,52 @@
+ 'some_username',
+ 'email' => 'some_email@example.com',
+ 'password' => 'some_password',
+ ]);
+
+ $user = $model->signup();
+
+ $this->assertInstanceOf('common\models\User', $user, 'user should be valid');
+
+ expect('username should be correct', $user->username)->equals('some_username');
+ expect('email should be correct', $user->email)->equals('some_email@example.com');
+ expect('password should be correct', $user->validatePassword('some_password'))->true();
+ }
+
+ public function testNotCorrectSignup()
+ {
+ $model = new SignupForm([
+ 'username' => 'troy.becker',
+ 'email' => 'nicolas.dianna@hotmail.com',
+ 'password' => 'some_password',
+ ]);
+
+ expect('username and email are in use, user should not be created', $model->signup())->null();
+ }
+
+ public function fixtures()
+ {
+ return [
+ 'user' => [
+ 'class' => UserFixture::className(),
+ 'dataFile' => '@tests/codeception/frontend/unit/fixtures/data/models/user.php',
+ ],
+ ];
+ }
+}
diff --git a/yii.bat b/yii.bat
new file mode 100644
index 0000000..d516b3a
--- /dev/null
+++ b/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem Yii command line bootstrap script for Windows.
+rem
+rem @author Qiang Xue
+rem @link http://www.yiiframework.com/
+rem @copyright Copyright (c) 2008 Yii Software LLC
+rem @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
--
libgit2 0.21.4