+
+ = (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 @@
+
+
+ = 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/modules/comment/Controller.php b/common/modules/comment/Controller.php
new file mode 100644
index 0000000..d0ce56f
--- /dev/null
+++ b/common/modules/comment/Controller.php
@@ -0,0 +1,96 @@
+ [
+ 'class' => \yii\filters\VerbFilter::className(),
+ 'actions' => [
+ '*' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ public function actionDelete()
+ {
+ \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+ $post = \Yii::$app->request->post('Comment');
+ if(!empty($post['comment_id'])) {
+ if($model = \common\modules\comment\models\Comment::findOne($post['comment_id'])) {
+ /**
+ * @var \common\modules\comment\models\Comment $model
+ */
+ $model->scenario = is_int(\Yii::$app->user->getId()) ? \common\modules\comment\models\Comment::SCENARIO_USER : \common\modules\comment\models\Comment::SCENARIO_GUEST;
+ if($model->deleteComment()) {
+ \Yii::$app->response->data = ['text' => 'Comment marked as deleted and will be check by administrators'];
+ } else {
+ \Yii::$app->response->data = ['error' => $model->hasErrors('comment_id')?$model->getFirstError('comment_id'):'Cannot delete message'];
+ }
+ }else {
+ \Yii::$app->response->data = ['error' => 'Comment not found'];
+ };
+ } else {
+ \Yii::$app->response->data = ['error' => 'Missing comment_id'];
+ }
+ \Yii::$app->response->send();
+ }
+
+ public function actionUpdate()
+ {
+ \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+ $post = \Yii::$app->request->post();
+ if(!empty($post['Comment']['comment_id'])) {
+ if($model = \common\modules\comment\models\Comment::findOne($post['Comment']['comment_id'])) {
+ /**
+ * @var \common\modules\comment\models\Comment $model
+ */
+ $model->scenario = is_int(\Yii::$app->user->getId()) ? \common\modules\comment\models\Comment::SCENARIO_USER : \common\modules\comment\models\Comment::SCENARIO_GUEST;
+ $model->load($post);
+ if(empty($post['Comment']['comment_pid'])) {
+ $model->comment_pid = null;
+ }
+ if($model->updateComment()) {
+ \Yii::$app->response->data = ['text' => 'Comment successfully updated'];
+ } else {
+ \Yii::$app->response->data = ['error' => $model->hasErrors()?$model->getFirstErrors():'Cannot update message'];
+ }
+ }else {
+ \Yii::$app->response->data = ['error' => 'Comment not found'];
+ };
+ } else {
+ \Yii::$app->response->data = ['error' => 'Missing comment_id'];
+ }
+ \Yii::$app->response->send();
+ }
+
+ public function actionForm()
+ {
+ $post = \Yii::$app->request->post('Comment');
+ if(!empty($post['comment_id'])) {
+ $model = \common\modules\comment\models\Comment::find()->where(['comment_id' => $post['comment_id']])->with('parent', 'author')->one();
+ if($model) {
+ /**
+ * @var \common\modules\comment\models\Comment $model
+ */
+ $model->scenario = is_int(\Yii::$app->user->getId()) ? \common\modules\comment\models\Comment::SCENARIO_USER : \common\modules\comment\models\Comment::SCENARIO_GUEST;
+ if($model->checkUpdate()) {
+ return $this->renderAjax('@common/modules/comment/views/comment_form', [
+ 'model' => $model,
+ ]);
+ } else {
+ \Yii::$app->response->data = ['error' => 'You are not able to update this comment'];
+ }
+ }else {
+ \Yii::$app->response->data = ['error' => 'Comment not found'];
+ };
+ } else {
+ \Yii::$app->response->data = ['error' => 'Missing comment_id'];
+ }
+ \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+ \Yii::$app->response->send();
+ }
+ }
\ No newline at end of file
diff --git a/common/modules/comment/Module.php b/common/modules/comment/Module.php
new file mode 100644
index 0000000..6a18e72
--- /dev/null
+++ b/common/modules/comment/Module.php
@@ -0,0 +1,74 @@
+
+ * [
+ * 'rules' => [
+ * \full\namapaced\ClassName,
+ * \another\one\ClassName,
+ * ],
+ * 'permissions' => [
+ * [
+ * 'name' => stringName,
+ * 'description' => descriptionText,
+ * 'ruleName' => (new \full\namespaced\ClassName())->name (optional)
+ * ],
+ * [
+ * 'name' => stringName2,
+ * 'description' => descriptionText2,
+ * 'ruleName' => (new \another\one\ClassName())->name (optional)
+ * ],
+ * ]
+ * ]
+ *
+ *
+ * @var array
+ * @see \common\modules\comment\commands\RbacController
+ */
+ public $rbac = [];
+
+ /**
+ * @var \yii\db\Connection Connection to the db
+ */
+ public $db = null;
+
+ /**
+ * @inheritdoc
+ */
+ public function init()
+ {
+ parent::init();
+ if(\Yii::$app instanceof \yii\console\Application) {
+ $this->controllerNamespace = 'common\modules\comment\commands';
+ }
+ if($this->db === null) {
+ $this->db = \Yii::$app->db;
+ } elseif(!$this->db instanceof \yii\db\Connection) {
+ throw new \yii\base\InvalidConfigException('Конфиг db обязан наследоваться от'.\yii\db\Connection::className());
+ }
+ }
+ }
\ No newline at end of file
diff --git a/common/modules/comment/Permissions.php b/common/modules/comment/Permissions.php
new file mode 100644
index 0000000..965f0f4
--- /dev/null
+++ b/common/modules/comment/Permissions.php
@@ -0,0 +1,12 @@
+controller->module;
+ if(!$module->useRbac) {
+ throw new \yii\base\InvalidConfigException('Please set useRbac config to TRUE in your module configs');
+ }
+ $auth = \Yii::$app->getAuthManager();
+ if(!$auth instanceof \yii\rbac\ManagerInterface) {
+ throw new \yii\base\InvalidConfigException('ManagerInterface is not configured');
+ }
+ if(!empty($module->rbac['rules'])) {
+ foreach($module->rbac['rules'] as $rule) {
+ $rule_model = new $rule();
+ echo "Creating rule: ".$rule_model->name."\n";
+ if($auth->add($rule_model)) {
+ echo "Successful\n";
+ } else {
+ echo "Failed\n";
+ }
+ unset($rule_model);
+ }
+ }
+ if(!empty($module->rbac['permissions'])) {
+ foreach($module->rbac['permissions'] as $permission) {
+ echo "Creating permission: ".$permission['name']."\n";
+ if($auth->add(new \yii\rbac\Permission($permission))) {
+ echo "Successful\n";
+ } else {
+ echo "Failed\n";
+ }
+ }
+ }
+ }
+
+ public function actionUninstall()
+ {
+ /**
+ * @var \common\modules\comment\Module $module
+ */
+ $module = \Yii::$app->controller->module;
+ if(!$module->useRbac) {
+ throw new \yii\base\InvalidConfigException('Please set useRbac config to TRUE in your module configs');
+ }
+ $auth = \Yii::$app->getAuthManager();
+ if(!$auth instanceof \yii\rbac\ManagerInterface) {
+ throw new \yii\base\InvalidConfigException('ManagerInterface is not configured');
+ }
+ if(!empty($module->rbac['rules'])) {
+ foreach($module->rbac['rules'] as $rule) {
+ $rule_model = new $rule();
+ echo "Removing rule: ".$rule_model->name."\n";
+ if($auth->remove($rule_model)) {
+ echo "Successful\n";
+ } else {
+ echo "Failed\n";
+ }
+ unset($rule_model);
+ }
+ }
+ if(!empty($module->rbac['permissions'])) {
+ foreach($module->rbac['permissions'] as $permission) {
+ echo "Removing permission: ".$permission['name']."\n";
+ if($auth->remove(new \yii\rbac\Permission($permission))) {
+ echo "Successful\n";
+ } else {
+ echo "Failed\n";
+ }
+ }
+ }
+ }
+
+ }
\ No newline at end of file
diff --git a/common/modules/comment/interfaces/CommentInterface.php b/common/modules/comment/interfaces/CommentInterface.php
new file mode 100644
index 0000000..471dddf
--- /dev/null
+++ b/common/modules/comment/interfaces/CommentInterface.php
@@ -0,0 +1,11 @@
+ 1,
+ ],
+ [
+ [ 'comment_pid' ],
+ 'exist',
+ 'targetAttribute' => 'comment_id',
+ 'filter' => [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ],
+ ],
+ ];
+ }
+
+ public function scenarios()
+ {
+ return [
+ self::SCENARIO_GUEST => [
+ 'user_name',
+ 'user_email',
+ 'text',
+ 'comment_pid',
+ ],
+ self::SCENARIO_USER => [
+ 'text',
+ 'comment_pid',
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function behaviors()
+ {
+ return [
+ [
+ 'class' => \yii\behaviors\TimestampBehavior::className(),
+ 'createdAtAttribute' => 'date_add',
+ 'updatedAtAttribute' => 'date_update',
+ 'value' => new \yii\db\Expression('NOW()'),
+ ],
+ ];
+ }
+
+ public static function tableName()
+ {
+ return '{{%comment}}';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'text' => \Yii::t('app', 'Комментарий'),
+ 'user_name' => \Yii::t('app', 'Имя'),
+ 'user_email' => \Yii::t('app', 'Email'),
+ ];
+ }
+
+ public function getGuestComment()
+ {
+ return $this->guestComment;
+ }
+
+ public function setGuestComment($value)
+ {
+ $this->guestComment = $value;
+ }
+
+ /**
+ * @param string $model
+ * @param integer $model_id
+ *
+ * @return ActiveQuery
+ */
+ public function getComments($model, $model_id)
+ {
+ return $this->find()
+ ->where([
+ 'comment.model' => $model,
+ 'comment.model_id' => $model_id,
+ 'comment.status' => 1,
+ ]);
+ }
+
+ public function postComment()
+ {
+ if($this->checkCreate()) {
+ if($this->insert()) {
+ $this->clearSafe();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ $this->addError('comment_id', 'You can`t post comment here');
+ return false;
+ }
+ }
+
+ public function updateComment()
+ {
+ if($this->checkUpdate()) {
+ if(empty( $this->comment_id )) {
+ $this->addError('comment_id', 'Comment ID not found');
+ return false;
+ } else {
+ if($this->update()) {
+ $this->clearSafe();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ } else {
+ $this->addError('comment_id', 'You can`t update this post');
+ return false;
+ }
+ }
+
+ public function deleteComment()
+ {
+ if($this->checkDelete()) {
+ if(empty( $this->comment_id )) {
+ $this->addError('comment_id', 'Comment ID not found');
+ return false;
+ } else {
+ if($this->status == self::STATUS_DELETED) {
+ return false;
+ }
+ $this->status = self::STATUS_DELETED;
+ if($this->update()) {
+ $this->clearSafe();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ } else {
+ $this->addError('comment_id', 'You can`t delete this post');
+ return false;
+ }
+ }
+
+ public function checkCreate()
+ {
+ if($this->getGuestComment()) {
+ return true;
+ } else {
+ return \Yii::$app->user->can(\common\modules\comment\Permissions::CREATE, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]);
+ }
+ }
+
+ public function checkUpdate()
+ {
+ if($this->scenario == self::SCENARIO_GUEST) {
+ return false;
+ } else {
+ return \Yii::$app->user->can(\common\modules\comment\Permissions::UPDATE, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]) || \Yii::$app->user->can(\common\modules\comment\Permissions::UPDATE_OWN, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]);
+ }
+ }
+
+ public function checkDelete()
+ {
+ if($this->scenario == self::SCENARIO_GUEST) {
+ return false;
+ } else {
+ return \Yii::$app->user->can(\common\modules\comment\Permissions::DELETE, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]) || \Yii::$app->user->can(\common\modules\comment\Permissions::DELETE_OWN, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]);
+ }
+ }
+
+ protected function clearSafe($setNew = true)
+ {
+ $safe = $this->safeAttributes();
+ $count = count($safe);
+ $values = array_fill(0, $count, NULL);
+ $result = array_combine($safe, $values);
+ $this->setAttributes($result);
+ $this->setIsNewRecord($setNew);
+ }
+
+ public function getParent()
+ {
+ return $this->hasOne(self::className(), [ 'comment_id' => 'comment_pid' ]);
+ }
+
+ public function getAuthor()
+ {
+ // if($this->user_id != NULL) {
+ return $this->hasOne(\common\models\User::className(), [ 'id' => 'user_id' ]);
+ // } else {
+ // return ['firstname' => $this->user_name, 'email' => $this->user_email];
+ // }
+ }
+
+ public function checkRating()
+ {
+ $rating = $this->hasOne(\common\modules\comment\models\Rating::className(), [
+ 'model_id' => 'comment_id',
+ ])
+ ->andWhere([
+ 'model' => $this->className(),
+ ])
+ ->one();
+ if(!$rating instanceof \common\modules\comment\models\Rating) {
+ $rating = new \common\modules\comment\models\Rating([
+ 'model' => $this->className(),
+ 'model_id' => $this->comment_id,
+ 'user_id' => $this->user_id,
+ ]);
+ $rating->save();
+ }
+ }
+
+ public function getRating()
+ {
+ $this->checkRating();
+ return $this->hasOne(\common\modules\comment\models\Rating::className(), [
+ 'model_id' => 'comment_id',
+ ])
+ ->andWhere([ 'model' => $this->className() ]);
+ }
+
+ public function hasRating($return = true)
+ {
+ $rating = $this->hasOne(\common\modules\comment\models\Rating::className(), [
+ 'model_id' => 'comment_id',
+ ])
+ ->andWhere([ 'model' => $this->className() ])
+ ->andWhere([
+ 'not',
+ [ 'value' => NULL ],
+ ])
+ ->one();
+ if($return) {
+ return $rating;
+ } else {
+ return $rating ? true : false;
+ }
+ }
+
+ }
diff --git a/common/modules/comment/models/CommentProject.php b/common/modules/comment/models/CommentProject.php
new file mode 100644
index 0000000..7cc3793
--- /dev/null
+++ b/common/modules/comment/models/CommentProject.php
@@ -0,0 +1,328 @@
+ 0,
+ ],
+ [
+ [
+ 'budget_currency',
+ ],
+ 'default',
+ 'value' => 3,
+ ],
+ [
+ [ 'budget_currency' ],
+ 'exist',
+ 'targetClass' => Currency::className(),
+ 'targetAttribute' => 'currency_id',
+ ],
+ [
+ [
+ 'files',
+ ],
+ 'string',
+ ],
+ [
+ [
+ 'file',
+ ],
+ 'safe',
+ ],
+ [
+ [ 'status' ],
+ 'default',
+ 'value' => 1,
+ ],
+ ];
+ }
+
+ public function scenarios()
+ {
+ return [
+ self::SCENARIO_USER => [
+ 'text',
+ 'budget_from',
+ 'budget_to',
+ 'term_from',
+ 'term_to',
+ 'file',
+ ],
+ self::SCENARIO_GUEST => [
+
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function behaviors()
+ {
+ return [
+ [
+ 'class' => \yii\behaviors\TimestampBehavior::className(),
+ 'createdAtAttribute' => 'date_add',
+ 'updatedAtAttribute' => 'date_update',
+ 'value' => new \yii\db\Expression('NOW()'),
+ ],
+ ];
+ }
+
+ public static function tableName()
+ {
+ return '{{%comment_project}}';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'text' => \Yii::t('app', 'Текст ответа'),
+ 'budget_from' => \Yii::t('app', 'от'),
+ 'budget_to' => \Yii::t('app', 'до'),
+ 'term_from' => \Yii::t('app', 'от'),
+ 'term_to' => \Yii::t('app', 'до'),
+ ];
+ }
+
+ public function getGuestComment()
+ {
+ return $this->guestComment;
+ }
+
+// public function setGuestComment($value)
+// {
+// $this->guestComment = $value;
+// }
+
+ /**
+ * @param string $model
+ * @param integer $model_id
+ *
+ * @return ActiveQuery
+ */
+ public function getComments($model, $model_id)
+ {
+ return $this->find()
+ ->where([
+ 'comment_project.model' => $model,
+ 'comment_project.model_id' => $model_id,
+ 'comment_project.status' => 1,
+ ]);
+ }
+
+ public function postComment()
+ {
+ if($this->checkCreate()) {
+ if(!empty(\Yii::$app->request->post($this->formName())['anonymous'])) {
+ $this->status = self::STATUS_ANONYMOUS;
+ }
+ $this->file = UploadedFile::getInstances($this, 'file');
+ if(!empty($this->file)) {
+ $file_id = [];
+ if(is_array($this->file)){
+ foreach($this->file as $file){
+ if($file instanceof UploadedFile){
+ $file_model = new File();
+ $file_id[] = $file_model->saveFile($file);
+ }
+ }
+ } else {
+ if($this->file instanceof UploadedFile){
+ $file_model = new File();
+ $file_id[] = $file_model->saveFile($this->file);
+ }
+ }
+ $this->files = json_encode($file_id);
+ }
+ if($this->insert()) {
+ $this->clearSafe();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ $this->addError('comment_id', 'You can`t post comment here');
+ return false;
+ }
+ }
+
+ public function updateComment()
+ {
+ if($this->checkUpdate()) {
+ if(empty( $this->comment_id )) {
+ $this->addError('comment_id', 'Comment ID not found');
+ return false;
+ } else {
+ if($this->update()) {
+ $this->clearSafe();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ } else {
+ $this->addError('comment_id', 'You can`t update this post');
+ return false;
+ }
+ }
+
+ public function deleteComment()
+ {
+ if($this->checkDelete()) {
+ if(empty( $this->comment_id )) {
+ $this->addError('comment_id', 'Comment ID not found');
+ return false;
+ } else {
+ if($this->status == self::STATUS_DELETED) {
+ return false;
+ }
+ $this->status = self::STATUS_DELETED;
+ if($this->update()) {
+ $this->clearSafe();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ } else {
+ $this->addError('comment_id', 'You can`t delete this post');
+ return false;
+ }
+ }
+
+ public function checkCreate()
+ {
+ if($this->getGuestComment()) {
+ return true;
+ } else {
+ return \Yii::$app->user->can(\common\modules\comment\Permissions::CREATE, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]);
+ }
+ }
+
+ public function checkUpdate()
+ {
+ if($this->scenario == self::SCENARIO_GUEST) {
+ return false;
+ } else {
+ return \Yii::$app->user->can(\common\modules\comment\Permissions::UPDATE, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]) || \Yii::$app->user->can(\common\modules\comment\Permissions::UPDATE_OWN, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]);
+ }
+ }
+
+ public function checkDelete()
+ {
+ if($this->scenario == self::SCENARIO_GUEST) {
+ return false;
+ } else {
+ return \Yii::$app->user->can(\common\modules\comment\Permissions::DELETE, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]) || \Yii::$app->user->can(\common\modules\comment\Permissions::DELETE_OWN, [
+ 'model' => $this->model,
+ 'model_id' => $this->model_id,
+ ]);
+ }
+ }
+
+ protected function clearSafe($setNew = true)
+ {
+ $safe = $this->safeAttributes();
+ $count = count($safe);
+ $values = array_fill(0, $count, NULL);
+ $result = array_combine($safe, $values);
+ $this->setAttributes($result);
+ $this->setIsNewRecord($setNew);
+ }
+
+ public function getAuthor()
+ {
+ // if($this->user_id != NULL) {
+ return $this->hasOne(\common\models\User::className(), [ 'id' => 'user_id' ]);
+ // } else {
+ // return ['firstname' => $this->user_name, 'email' => $this->user_email];
+ // }
+ }
+
+ }
diff --git a/common/modules/comment/models/Rating.php b/common/modules/comment/models/Rating.php
new file mode 100644
index 0000000..3222732
--- /dev/null
+++ b/common/modules/comment/models/Rating.php
@@ -0,0 +1,64 @@
+ Yii::t('app', 'Rating ID'),
+ 'date_add' => Yii::t('app', 'Date Add'),
+ 'date_update' => Yii::t('app', 'Date Update'),
+ 'user_id' => Yii::t('app', 'User ID'),
+ 'entity' => Yii::t('app', 'Entity'),
+ 'value' => Yii::t('app', 'Value'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getUser()
+ {
+ return $this->hasOne(\common\models\User::className(), ['id' => 'user_id']);
+ }
+}
diff --git a/common/modules/comment/rbac/ArtboxCommentCreateRule.php b/common/modules/comment/rbac/ArtboxCommentCreateRule.php
new file mode 100644
index 0000000..6f481fc
--- /dev/null
+++ b/common/modules/comment/rbac/ArtboxCommentCreateRule.php
@@ -0,0 +1,17 @@
+'+data.text+'');
+ $(container).remove();
+ } else {
+ $(container).prepend('",
+ options: {
+ disabled: false,
+
+ // callbacks
+ create: null
+ },
+ _createWidget: function( options, element ) {
+ element = $( element || this.defaultElement || this )[ 0 ];
+ this.element = $( element );
+ this.uuid = widget_uuid++;
+ this.eventNamespace = "." + this.widgetName + this.uuid;
+
+ this.bindings = $();
+ this.hoverable = $();
+ this.focusable = $();
+
+ if ( element !== this ) {
+ $.data( element, this.widgetFullName, this );
+ this._on( true, this.element, {
+ remove: function( event ) {
+ if ( event.target === element ) {
+ this.destroy();
+ }
+ }
+ });
+ this.document = $( element.style ?
+ // element within the document
+ element.ownerDocument :
+ // element is window or document
+ element.document || element );
+ this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
+ }
+
+ this.options = $.widget.extend( {},
+ this.options,
+ this._getCreateOptions(),
+ options );
+
+ this._create();
+ this._trigger( "create", null, this._getCreateEventData() );
+ this._init();
+ },
+ _getCreateOptions: $.noop,
+ _getCreateEventData: $.noop,
+ _create: $.noop,
+ _init: $.noop,
+
+ destroy: function() {
+ this._destroy();
+ // we can probably remove the unbind calls in 2.0
+ // all event bindings should go through this._on()
+ this.element
+ .unbind( this.eventNamespace )
+ .removeData( this.widgetFullName )
+ // support: jquery <1.6.3
+ // http://bugs.jquery.com/ticket/9413
+ .removeData( $.camelCase( this.widgetFullName ) );
+ this.widget()
+ .unbind( this.eventNamespace )
+ .removeAttr( "aria-disabled" )
+ .removeClass(
+ this.widgetFullName + "-disabled " +
+ "ui-state-disabled" );
+
+ // clean up events and states
+ this.bindings.unbind( this.eventNamespace );
+ this.hoverable.removeClass( "ui-state-hover" );
+ this.focusable.removeClass( "ui-state-focus" );
+ },
+ _destroy: $.noop,
+
+ widget: function() {
+ return this.element;
+ },
+
+ option: function( key, value ) {
+ var options = key,
+ parts,
+ curOption,
+ i;
+
+ if ( arguments.length === 0 ) {
+ // don't return a reference to the internal hash
+ return $.widget.extend( {}, this.options );
+ }
+
+ if ( typeof key === "string" ) {
+ // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
+ options = {};
+ parts = key.split( "." );
+ key = parts.shift();
+ if ( parts.length ) {
+ curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
+ for ( i = 0; i < parts.length - 1; i++ ) {
+ curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
+ curOption = curOption[ parts[ i ] ];
+ }
+ key = parts.pop();
+ if ( arguments.length === 1 ) {
+ return curOption[ key ] === undefined ? null : curOption[ key ];
+ }
+ curOption[ key ] = value;
+ } else {
+ if ( arguments.length === 1 ) {
+ return this.options[ key ] === undefined ? null : this.options[ key ];
+ }
+ options[ key ] = value;
+ }
+ }
+
+ this._setOptions( options );
+
+ return this;
+ },
+ _setOptions: function( options ) {
+ var key;
+
+ for ( key in options ) {
+ this._setOption( key, options[ key ] );
+ }
+
+ return this;
+ },
+ _setOption: function( key, value ) {
+ this.options[ key ] = value;
+
+ if ( key === "disabled" ) {
+ this.widget()
+ .toggleClass( this.widgetFullName + "-disabled", !!value );
+
+ // If the widget is becoming disabled, then nothing is interactive
+ if ( value ) {
+ this.hoverable.removeClass( "ui-state-hover" );
+ this.focusable.removeClass( "ui-state-focus" );
+ }
+ }
+
+ return this;
+ },
+
+ enable: function() {
+ return this._setOptions({ disabled: false });
+ },
+ disable: function() {
+ return this._setOptions({ disabled: true });
+ },
+
+ _on: function( suppressDisabledCheck, element, handlers ) {
+ var delegateElement,
+ instance = this;
+
+ // no suppressDisabledCheck flag, shuffle arguments
+ if ( typeof suppressDisabledCheck !== "boolean" ) {
+ handlers = element;
+ element = suppressDisabledCheck;
+ suppressDisabledCheck = false;
+ }
+
+ // no element argument, shuffle and use this.element
+ if ( !handlers ) {
+ handlers = element;
+ element = this.element;
+ delegateElement = this.widget();
+ } else {
+ element = delegateElement = $( element );
+ this.bindings = this.bindings.add( element );
+ }
+
+ $.each( handlers, function( event, handler ) {
+ function handlerProxy() {
+ // allow widgets to customize the disabled handling
+ // - disabled as an array instead of boolean
+ // - disabled class as method for disabling individual parts
+ if ( !suppressDisabledCheck &&
+ ( instance.options.disabled === true ||
+ $( this ).hasClass( "ui-state-disabled" ) ) ) {
+ return;
+ }
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
+ .apply( instance, arguments );
+ }
+
+ // copy the guid so direct unbinding works
+ if ( typeof handler !== "string" ) {
+ handlerProxy.guid = handler.guid =
+ handler.guid || handlerProxy.guid || $.guid++;
+ }
+
+ var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
+ eventName = match[1] + instance.eventNamespace,
+ selector = match[2];
+ if ( selector ) {
+ delegateElement.delegate( selector, eventName, handlerProxy );
+ } else {
+ element.bind( eventName, handlerProxy );
+ }
+ });
+ },
+
+ _off: function( element, eventName ) {
+ eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
+ this.eventNamespace;
+ element.unbind( eventName ).undelegate( eventName );
+
+ // Clear the stack to avoid memory leaks (#10056)
+ this.bindings = $( this.bindings.not( element ).get() );
+ this.focusable = $( this.focusable.not( element ).get() );
+ this.hoverable = $( this.hoverable.not( element ).get() );
+ },
+
+ _delay: function( handler, delay ) {
+ function handlerProxy() {
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
+ .apply( instance, arguments );
+ }
+ var instance = this;
+ return setTimeout( handlerProxy, delay || 0 );
+ },
+
+ _hoverable: function( element ) {
+ this.hoverable = this.hoverable.add( element );
+ this._on( element, {
+ mouseenter: function( event ) {
+ $( event.currentTarget ).addClass( "ui-state-hover" );
+ },
+ mouseleave: function( event ) {
+ $( event.currentTarget ).removeClass( "ui-state-hover" );
+ }
+ });
+ },
+
+ _focusable: function( element ) {
+ this.focusable = this.focusable.add( element );
+ this._on( element, {
+ focusin: function( event ) {
+ $( event.currentTarget ).addClass( "ui-state-focus" );
+ },
+ focusout: function( event ) {
+ $( event.currentTarget ).removeClass( "ui-state-focus" );
+ }
+ });
+ },
+
+ _trigger: function( type, event, data ) {
+ var prop, orig,
+ callback = this.options[ type ];
+
+ data = data || {};
+ event = $.Event( event );
+ event.type = ( type === this.widgetEventPrefix ?
+ type :
+ this.widgetEventPrefix + type ).toLowerCase();
+ // the original event may come from any element
+ // so we need to reset the target on the new event
+ event.target = this.element[ 0 ];
+
+ // copy original event properties over to the new event
+ orig = event.originalEvent;
+ if ( orig ) {
+ for ( prop in orig ) {
+ if ( !( prop in event ) ) {
+ event[ prop ] = orig[ prop ];
+ }
+ }
+ }
+
+ this.element.trigger( event, data );
+ return !( $.isFunction( callback ) &&
+ callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
+ event.isDefaultPrevented() );
+ }
+};
+
+$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
+ $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
+ if ( typeof options === "string" ) {
+ options = { effect: options };
+ }
+ var hasOptions,
+ effectName = !options ?
+ method :
+ options === true || typeof options === "number" ?
+ defaultEffect :
+ options.effect || defaultEffect;
+ options = options || {};
+ if ( typeof options === "number" ) {
+ options = { duration: options };
+ }
+ hasOptions = !$.isEmptyObject( options );
+ options.complete = callback;
+ if ( options.delay ) {
+ element.delay( options.delay );
+ }
+ if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
+ element[ method ]( options );
+ } else if ( effectName !== method && element[ effectName ] ) {
+ element[ effectName ]( options.duration, options.easing, callback );
+ } else {
+ element.queue(function( next ) {
+ $( this )[ method ]();
+ if ( callback ) {
+ callback.call( element[ 0 ] );
+ }
+ next();
+ });
+ }
+ };
+});
+
+var widget = $.widget;
+
+
+
+}));
diff --git a/common/modules/file/config.php b/common/modules/file/config.php
new file mode 100644
index 0000000..d0b9c34
--- /dev/null
+++ b/common/modules/file/config.php
@@ -0,0 +1,3 @@
+$w){
+ return true;
+ }else if($height >$h) {
+ return true;
+ }
+ return false;
+ }
+
+
+ private function getUserPath(){
+ if(isset(Yii::$app->user->id)){
+ return 'user_'.Yii::$app->user->id;
+ }else {
+ return 'guest';
+ }
+ }
+
+ private function resizeImg($w, $h, $imageAlias,$imageAliasSave){
+ $img = Image::getImagine()->open(Yii::getAlias($imageAlias));
+
+ $size = $img->getSize();
+
+ $width = $size->getWidth();
+ $height = $size->getHeight();
+
+ $e_width = $w/$h;
+ $e_height = $h/$w;
+
+ $e1_width = $width/$height;
+ $e1_height = $height/$width;
+
+
+
+ if($e_width<$e1_width){
+
+ $new_width = $width*($e_width/$e1_width);
+
+ $y = 0;
+ $x = $width/ 2-($new_width/2);
+ $width = $new_width;
+
+ }else {
+
+ $new_height = $height*($e_height/$e1_height);
+ $x = 0;
+ $y = $height/2-($new_height/2);
+ $height = $new_height;
+ }
+
+
+
+
+ Image::crop($imageAlias, $width, $height,[$x,$y])
+ ->save(Yii::getAlias($imageAliasSave), ['quality' =>
+ 100]);
+
+
+ $imagine = new Imagine();
+ $imagine->open($imageAliasSave)
+ ->resize(new Box($w, $h))
+ ->save($imageAliasSave, array('flatten' => false));
+
+
+
+ }
+
+ private function deleteImages($old_img){
+
+ if(!empty($old_img) && file_exists($_SERVER['DOCUMENT_ROOT'].$old_img)){
+
+ $rootDir = explode("/", $old_img);
+
+ $row = $_SERVER['DOCUMENT_ROOT'].'/'.$rootDir[1].'/'.$rootDir[2].'/'.$rootDir[3].'/';
+
+ $allFiles = scandir($row);
+
+ $allFiles = array_slice($allFiles, 2);
+
+ foreach($allFiles as $oldFile){
+
+ unlink($row.$oldFile);
+
+ }
+
+ }
+ }
+
+ public function actionDeleteImage(){
+
+ $this->enableCsrfValidation = false;
+
+ $request = Yii::$app->request->post();
+
+ if($request){
+ if ($request['old_img']) {
+ $this->deleteImages($request['old_img']);
+ }
+ if(isset($request['action']) && $request['action']=='save'){
+ $object = str_replace('-', '\\',$request['model']);
+ $model = new $object;
+ $model = $model->findOne($request['id']);
+ $model->$request['field'] = $request['new_url'];
+ $model->save();
+ }
+ }
+
+ }
+
+
+ public function actionDownloadPhoto()
+ {
+
+ $model = new ImageSizerForm();
+
+ $request = Yii::$app->request->post();
+
+ if ($request) {
+
+ $model->multi = isset($request['multi'])? 1 : 0;
+
+ $model->file = UploadedFile::getInstance($model, 'file');
+
+ $md5_file = md5_file($model->file->tempName).rand(1, 1000);
+
+ $imgDir = Yii::getAlias('@storage/'.$this->getUserPath().'/'.$md5_file.'/');
+
+ $imageOrigAlias = Yii::getAlias($imgDir.'original'.'.'.$model->file->extension);
+
+ if(!is_dir($imgDir)) {
+ mkdir($imgDir, 0755, true);
+ }
+
+ $model->file->saveAs($imageOrigAlias);
+
+
+ if(isset($request['size'] )){
+
+ $request['size'] = ArrayHelper::toArray(json_decode($request['size']));
+
+ foreach($request['size'] as $size){
+ if($size['width'] && $size['height']){
+
+ $imageAlias = Yii::getAlias($imgDir.$size['width'].'x'.$size['height'].'.'.$model->file->extension);
+
+ $imageLink = '/storage/'.$this->getUserPath().'/'.$md5_file.'/'.$size['width'].'x'.$size['height'].'.'.$model->file->extension;
+
+ $this->resizeImg($size['width'], $size['height'], $imageOrigAlias,$imageAlias);
+
+ }
+ }
+
+ } else {
+
+ $imageLink = '/storage/'.$this->getUserPath().'/'.$md5_file.'/'.'original'.'.'.$model->file->extension;
+
+ }
+
+
+ if($model->multi){
+ $view = $this->renderPartial('/_gallery_item', [
+ 'item' => ['image'=>$imageLink],
+ 'field'=>$request['field']
+ ]);
+ return json_encode(['link'=>$imageLink,
+ 'view' =>$view,
+
+ ]);
+
+
+ } else {
+ $view = $this->renderPartial('/_one_item', [
+ 'item' => ['image'=>$imageLink],
+ 'field'=>$request['field']
+ ]);
+ return json_encode(['link'=>$imageLink,
+ 'view' =>$view,
+ ]);
+ }
+
+
+ }
+ }
+
+
+ public function getex($filename) {
+ return end(explode(".", $filename));
+ }
+
+
+ public function actionImagesUpload(){
+
+ if($_FILES['upload'])
+ {
+ if (($_FILES['upload'] == "none") OR (empty($_FILES['upload']['name'])) )
+ {
+ $message = "Вы не выбрали файл";
+ }
+ else if ($_FILES['upload']["size"] == 0 OR $_FILES['upload']["size"] > 2050000)
+ {
+ $message = "Размер файла не соответствует нормам";
+ }
+ else if (($_FILES['upload']["type"] != "image/jpeg") AND ($_FILES['upload']["type"] != "image/jpeg") AND ($_FILES['upload']["type"] != "image/png") AND ($_FILES['upload']['type'] != 'image/gif'))
+ {
+ $message = "Допускается загрузка только картинок JPG и PNG.";
+ }
+ else if (!is_uploaded_file($_FILES['upload']["tmp_name"]))
+ {
+ $message = "Что-то пошло не так. Попытайтесь загрузить файл ещё раз.";
+ }
+ else{
+ $name =$_FILES['upload']['name'].'.'.$this->getex($_FILES['upload']['name']);
+
+ $path = "../../storage/".$this->getUserPath()."/images/";
+ if(!is_dir($path)) {
+ mkdir($path, 0755, true);
+ }
+
+
+
+ move_uploaded_file($_FILES['upload']['tmp_name'], $path.$name);
+
+ $full_path = '/storage/'.$this->getUserPath().'/images/'.$name;
+
+ $message = "Файл ".$_FILES['upload']['name']." загружен";
+
+
+ }
+ $callback = $_REQUEST['CKEditorFuncNum'];
+ echo '';
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/common/modules/file/models/ImageSizerForm.php b/common/modules/file/models/ImageSizerForm.php
new file mode 100644
index 0000000..4f141fb
--- /dev/null
+++ b/common/modules/file/models/ImageSizerForm.php
@@ -0,0 +1,38 @@
+ 255],
+ [['model', 'form',], 'string'],
+ [['file','img','price_list'], 'file'],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/common/modules/file/views/_gallery_item.php b/common/modules/file/views/_gallery_item.php
new file mode 100644
index 0000000..f04a44d
--- /dev/null
+++ b/common/modules/file/views/_gallery_item.php
@@ -0,0 +1,9 @@
+
+
+
+ = Html::img($item['image'])?>
+
+
\ No newline at end of file
diff --git a/common/modules/file/views/_one_item.php b/common/modules/file/views/_one_item.php
new file mode 100644
index 0000000..4534483
--- /dev/null
+++ b/common/modules/file/views/_one_item.php
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+ = Html::a( Html::img($item['image']),'#',['class'=>'thumbnail']) ?>
+
+
+
diff --git a/common/modules/file/widgets/ImageUploader.php b/common/modules/file/widgets/ImageUploader.php
new file mode 100644
index 0000000..48d7787
--- /dev/null
+++ b/common/modules/file/widgets/ImageUploader.php
@@ -0,0 +1,63 @@
+render('image_sizer',
+ [
+ 'model'=>$this->model,
+ 'size' => $this->size,
+ 'field' => $this->field,
+ 'height' => $this->height,
+ 'width' => $this->width,
+ 'multi' => $this->multi,
+ 'name' => $this->name,
+ 'remover' => $this->remover
+ ]);
+
+ }
+
+ public function getGallery(){
+ if($this->gallery){
+ $array = explode(",", $this->gallery);
+ if(count($array) > 1){
+ array_pop($array);
+ }
+ return $array;
+ } else {
+ return array();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/common/modules/file/widgets/views/image_sizer.php b/common/modules/file/widgets/views/image_sizer.php
new file mode 100644
index 0000000..510f883
--- /dev/null
+++ b/common/modules/file/widgets/views/image_sizer.php
@@ -0,0 +1,203 @@
+tableSchema->primaryKey[0];
+
+?>
+
+
+
+
+ = Html::activeHiddenInput( $model,$field,['id' => "{$field}_picture_link"]) ?>
+
+
+
+
+
+
+
+
+
+ $field): ?>
+
+
+
+ $field):?>
+ = Html::a(Html::img($model->$field),'#',['class'=>'thumbnail']) ?>
+
+
+
+
+
+
+
+
+ =$name?>
+
+ = Html::activeFileInput( new ImageSizerForm(),'file',['id'=>$field, 'data-url'=>Yii::$app->getUrlManager()->createUrl('file/uploader/download-photo')]);?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ =$name?>
+
+ = Html::activeFileInput( new ImageSizerForm(),'file',['id'=>$field, 'data-url'=>Yii::$app->getUrlManager()->createUrl('file/uploader/download-photo'), 'multiple'=> 'multiple' ]);?>
+
+
+ = Html::activeHiddenInput( $model,$field,['id' => "{$field}_picture_link"]) ?>
+
+
+
+
+
+ context->getGallery() as $image){
+ echo $this->render('@common/modules/file/views/_gallery_item', [ 'item' => ['image'=>$image]]);
+ }
+ ?>
+
+
+
+
diff --git a/common/modules/product/Module.php b/common/modules/product/Module.php
new file mode 100644
index 0000000..4cc65a6
--- /dev/null
+++ b/common/modules/product/Module.php
@@ -0,0 +1,24 @@
+ [
+ 'category_group' => 1,
+ 'brand_group' => 2,
+ ],
+];
\ No newline at end of file
diff --git a/common/modules/product/controllers/DefaultController.php b/common/modules/product/controllers/DefaultController.php
new file mode 100644
index 0000000..609a4c4
--- /dev/null
+++ b/common/modules/product/controllers/DefaultController.php
@@ -0,0 +1,20 @@
+render('index');
+ }
+}
diff --git a/common/modules/product/controllers/ManageController.php b/common/modules/product/controllers/ManageController.php
new file mode 100644
index 0000000..fdfe194
--- /dev/null
+++ b/common/modules/product/controllers/ManageController.php
@@ -0,0 +1,124 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all Product models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new ProductSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single Product model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new Product model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new Product();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->product_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing Product 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->product_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing Product 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 Product model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Product the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = Product::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/product/helpers/ProductHelper.php b/common/modules/product/helpers/ProductHelper.php
new file mode 100644
index 0000000..fc2b49c
--- /dev/null
+++ b/common/modules/product/helpers/ProductHelper.php
@@ -0,0 +1,24 @@
+getModule('product')->params['category_group'];
+ }
+
+ public static function getBrandGroupId() {
+ return \Yii::$app->getModule('product')->params['brand_group'];
+ }
+
+ public static function getCategories() {
+ return TaxOption::find()->getTree(self::getCategoryGroupId());
+ }
+
+ public static function getBrands() {
+ return TaxOption::find()->where(['tax_group_id' => self::getBrandGroupId()]);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/product/models/Product.php b/common/modules/product/models/Product.php
new file mode 100644
index 0000000..1fbc8f7
--- /dev/null
+++ b/common/modules/product/models/Product.php
@@ -0,0 +1,106 @@
+ relationBehavior::className(),
+ 'relations' => [
+ 'product_categories' => 'entity1' // Product category
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return '{{%product}}';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['tax_brand_id'], 'integer'],
+ [['name'], 'string', 'max' => 150],
+ [['categories'], 'safe'],
+// [['product_id'], 'exist', 'skipOnError' => true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_id' => Yii::t('product', 'Product ID'),
+ 'name' => Yii::t('product', 'Name'),
+ 'tax_brand_id' => Yii::t('product', 'Brand'),
+ 'brand' => Yii::t('product', 'Brand'),
+ 'categories' => Yii::t('product', 'Categories'), // relation behavior field
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getBrand()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'tax_brand_id']);
+ }
+
+ public function getFullName()
+ {
+ return $this->brandname .' '. $this->name;
+ }
+
+ public function getBrandName()
+ {
+ return $this->getBrand()->one()->valueRenderHTML;
+ }
+
+ public function getCategory() {
+ /** @var ActiveQuery $categories */
+ $categories = $this->getRelations('product_categories');
+ $count = $categories->count();
+ if ($count == 0)
+ return 'None';
+ return $categories->one()->ValueRenderFlash . ($count > 1 ? ' + '. $count : '');
+ }
+
+ /**
+ * @inheritdoc
+ * @return ProductQuery the active query used by this AR class.
+ */
+ public static function find()
+ {
+ return new ProductQuery(get_called_class());
+ }
+}
diff --git a/common/modules/product/models/ProductCategory.php b/common/modules/product/models/ProductCategory.php
new file mode 100644
index 0000000..a5d1245
--- /dev/null
+++ b/common/modules/product/models/ProductCategory.php
@@ -0,0 +1,60 @@
+ true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['dev_category_id' => 'tax_option_id']],
+ [['product_id'], 'exist', 'skipOnError' => true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_id' => Yii::t('product', 'Product'),
+ 'dev_category_id' => Yii::t('product', 'Category'),
+ ];
+ }
+
+ public function getProduct() {
+ return $this->getEntity1();
+ }
+
+ public function getCategory() {
+ return $this->getEntity2();
+ }
+}
diff --git a/common/modules/product/models/ProductQuery.php b/common/modules/product/models/ProductQuery.php
new file mode 100644
index 0000000..373c74b
--- /dev/null
+++ b/common/modules/product/models/ProductQuery.php
@@ -0,0 +1,34 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @inheritdoc
+ * @return Product[]|array
+ */
+ public function all($db = null)
+ {
+ return parent::all($db);
+ }
+
+ /**
+ * @inheritdoc
+ * @return Product|array|null
+ */
+ public function one($db = null)
+ {
+ return parent::one($db);
+ }
+}
diff --git a/common/modules/product/models/ProductSearch.php b/common/modules/product/models/ProductSearch.php
new file mode 100644
index 0000000..4d2c42d
--- /dev/null
+++ b/common/modules/product/models/ProductSearch.php
@@ -0,0 +1,70 @@
+ $query,
+ ]);
+
+ $this->load($params);
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'tax_brand_id' => $this->tax_brand_id,
+ 'product_id' => $this->product_id,
+ ]);
+
+ $query->andFilterWhere(['like', 'name', $this->name]);
+
+ return $dataProvider;
+ }
+}
diff --git a/common/modules/product/views/default/index.php b/common/modules/product/views/default/index.php
new file mode 100644
index 0000000..218cc44
--- /dev/null
+++ b/common/modules/product/views/default/index.php
@@ -0,0 +1,12 @@
+
+
= $this->context->action->uniqueId ?>
+
+ This is the view content for action "= $this->context->action->id ?>".
+ The action belongs to the controller "= get_class($this->context) ?>"
+ in the "= $this->context->module->id ?>" module.
+
+
+ You may customize this page by editing the following file:
+ = __FILE__ ?>
+
+
diff --git a/common/modules/product/views/manage/_form.php b/common/modules/product/views/manage/_form.php
new file mode 100644
index 0000000..f1233cb
--- /dev/null
+++ b/common/modules/product/views/manage/_form.php
@@ -0,0 +1,43 @@
+
+
+
diff --git a/common/modules/product/views/manage/_search.php b/common/modules/product/views/manage/_search.php
new file mode 100644
index 0000000..f051c6a
--- /dev/null
+++ b/common/modules/product/views/manage/_search.php
@@ -0,0 +1,31 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'name') ?>
+
+ = $form->field($model, 'tax_brand_id') ?>
+
+ = $form->field($model, 'product_id') ?>
+
+
+ = Html::submitButton(Yii::t('product', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('product', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/common/modules/product/views/manage/create.php b/common/modules/product/views/manage/create.php
new file mode 100644
index 0000000..e5f7902
--- /dev/null
+++ b/common/modules/product/views/manage/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('product', 'Create Product');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/manage/index.php b/common/modules/product/views/manage/index.php
new file mode 100644
index 0000000..82254a2
--- /dev/null
+++ b/common/modules/product/views/manage/index.php
@@ -0,0 +1,34 @@
+title = Yii::t('product', 'Products');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('product', 'Create Product'), ['create'], ['class' => 'btn btn-success']) ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+// 'product_id',
+ 'fullname',
+ 'brandname',
+ 'category',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
diff --git a/common/modules/product/views/manage/update.php b/common/modules/product/views/manage/update.php
new file mode 100644
index 0000000..9ff3851
--- /dev/null
+++ b/common/modules/product/views/manage/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('product', 'Update {modelClass}: ', [
+ 'modelClass' => 'Product',
+]) . ' ' . $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->product_id]];
+$this->params['breadcrumbs'][] = Yii::t('product', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/manage/view.php b/common/modules/product/views/manage/view.php
new file mode 100644
index 0000000..e6d9c7c
--- /dev/null
+++ b/common/modules/product/views/manage/view.php
@@ -0,0 +1,37 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('product', 'Update'), ['update', 'id' => $model->product_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('product', 'Delete'), ['delete', 'id' => $model->product_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'name',
+ 'tax_brand_id',
+ 'product_id',
+ ],
+ ]) ?>
+
+
diff --git a/common/modules/relation/Module.php b/common/modules/relation/Module.php
new file mode 100644
index 0000000..b4f79b1
--- /dev/null
+++ b/common/modules/relation/Module.php
@@ -0,0 +1,40 @@
+render('index');
+ }
+}
diff --git a/common/modules/relation/controllers/ManageController.php b/common/modules/relation/controllers/ManageController.php
new file mode 100644
index 0000000..2b797e4
--- /dev/null
+++ b/common/modules/relation/controllers/ManageController.php
@@ -0,0 +1,214 @@
+ $relation) {
+ $list[] = [
+ 'key' => $key,
+ 'name' => $relation['name'],
+ 'entity1_label' => $relation['entity1']['label'],
+ 'entity1_model' => $relation['entity1']['model'],
+ 'entity2_label' => $relation['entity2']['label'],
+ 'entity2_model' => $relation['entity2']['model'],
+ ];
+ }
+ return $this->render('relations', [
+ 'relations' => $list
+ ]);
+ }
+ /**
+ * Renders the pars view for
+ * @return string
+ */
+ public function actionPars($relation)
+ {
+ $relation_key = strtolower($relation);
+ $relation = relationHelper::getRelation($relation_key);
+
+ $dataProvider = new ActiveDataProvider([
+ 'query' => $relation['via']['model']::find(),
+ ]);
+
+ return $this->render('pars', [
+ 'dataProvider' => $dataProvider,
+ 'relation_key' => $relation_key,
+ 'relation' => $relation,
+ ]);
+ }
+
+ public function actionCreate($relation) {
+ $relation_key = strtolower($relation);
+ $relation = relationHelper::getRelation($relation_key);
+
+ $model = new $relation['via']['model'];
+
+ $query1 = $relation['entity1']['model']::find();
+ if (!empty($relation['entity1']['where']))
+ $query1->where($relation['entity1']['where']);
+
+ $query2 = $relation['entity2']['model']::find();
+ if (!empty($relation['entity2']['where']))
+ $query2->where($relation['entity2']['where']);
+
+ if ($model->load(Yii::$app->request->post())) {
+ $model->save();
+ return $this->redirect(['pars', 'relation' => $relation_key]);
+// return $this->redirect(['update', 'id' => $model->{$relation['entity1']['linked_key']}. ':' .$model->{$relation['entity2']['linked_key']}]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ 'items1' => $query1->all(),
+ 'items2' => $query2->all(),
+ 'relation_key' => $relation_key,
+ 'relation' => $relation,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing TaxGroup model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($relation, $id) {
+ $relation_key = strtolower($relation);
+ $relation = relationHelper::getRelation($relation_key);
+
+ list($id1, $id2) = explode(':', $id);
+
+ $model = $this->findModel($relation_key, $id1, $id2);
+
+ $query1 = $relation['entity1']['model']::find();
+ if (!empty($relation['entity1']['where']))
+ $query1->where($relation['entity1']['where']);
+
+ $query2 = $relation['entity2']['model']::find();
+ if (!empty($relation['entity2']['where']))
+ $query2->where($relation['entity2']['where']);
+
+ if ($model->load(Yii::$app->request->post())) {
+ $connection = Yii::$app->getDb();
+ $transaction = $connection->beginTransaction();
+ try {
+ // Delete links from viaTable
+ $connection->createCommand()
+ ->update
+ (
+ $relation['linked_table'],
+ [
+ $relation['entity1']['linked_key'] => $model->getAttribute($relation['entity1']['linked_key']),
+ $relation['entity2']['linked_key'] => $model->getAttribute($relation['entity2']['linked_key'])
+ ],
+ $this->getWhere($relation_key, $id1, $id2)
+ )
+ ->execute();
+ $transaction->commit();
+ } catch (Exception $ex) {
+ $transaction->rollback();
+ throw $ex;
+ }
+
+ return $this->redirect(['pars', 'relation' => $relation_key]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ 'items1' => $query1->all(),
+ 'items2' => $query2->all(),
+ 'relation_key' => $relation_key,
+ 'relation' => $relation,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing TaxGroup model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($relation, $id)
+ {
+ $relation_key = strtolower($relation);
+ $relation = relationHelper::getRelation($relation_key);
+
+ list($id1, $id2) = explode(':', $id);
+
+ $connection = Yii::$app->getDb();
+ $transaction = $connection->beginTransaction();
+ try {
+ // Delete links from viaTable
+ $connection->createCommand()
+ ->delete
+ (
+ $relation['linked_table'],
+ $this->getWhere($relation_key, $id1, $id2)
+ )
+ ->execute();
+ $transaction->commit();
+ } catch (Exception $ex) {
+ $transaction->rollback();
+ throw $ex;
+ }
+
+ return $this->redirect(['pars', 'relation' => $relation_key]);
+ }
+
+ /**
+ * Finds the based model for relation on its primaries keys value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param string $relation
+ * @param integer $id1
+ * @param integer $id2
+ * @return ActiveRecord the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($relation, $id1, $id2)
+ {
+ $relation_key = strtolower($relation);
+ $relation = relationHelper::getRelation($relation_key);
+ if (($model = $relation['via']['model']::findOne($this->getWhere($relation_key, $id1, $id2))) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+ protected function getWhere($relation_key, $id1, $id2) {
+ $relation = relationHelper::getRelation($relation_key);
+ // @todo Just think - if you need to search keys in the reverse order
+ $where = [
+ $relation['entity1']['linked_key'] => $id1,
+ $relation['entity2']['linked_key'] => $id2,
+ ];
+ if (!empty($relation['alias'])) {
+ $where[$relation['alias']] = $relation_key;
+ }
+ return $where;
+ }
+
+}
diff --git a/common/modules/relation/models/Relation.php b/common/modules/relation/models/Relation.php
new file mode 100644
index 0000000..bfe287f
--- /dev/null
+++ b/common/modules/relation/models/Relation.php
@@ -0,0 +1,78 @@
+ 50]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'alias' => Yii::t('relation', 'Alias'),
+ 'entity1_id' => Yii::t('relation', 'Entity1 ID'),
+ 'entity2_id' => Yii::t('relation', 'Entity2 ID'),
+ 'entity1' => Yii::t('relation', 'Entity1 ID'),
+ 'entity2' => Yii::t('relation', 'Entity2 ID'),
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ * @return RelationQuery the active query used by this AR class.
+ */
+ public static function find()
+ {
+ return new RelationQuery(get_called_class());
+ }
+
+ public function getRelationSection() {
+ return relationHelper::getRelation($this->alias);
+ }
+
+ public function getEntity1() {
+ return $this->getEntity('entity1');
+ }
+
+ public function getEntity2() {
+ return $this->getEntity('entity2');
+ }
+
+ protected function getEntity($entity) {
+ $relation = $this->getRelationSection();
+ if (!$relation)
+ return;
+ return $this->hasOne($relation[$entity]['model']::className(), [$relation[$entity]['key'] => $relation[$entity]['linked_key']]);
+ }
+}
diff --git a/common/modules/relation/models/RelationQuery.php b/common/modules/relation/models/RelationQuery.php
new file mode 100644
index 0000000..9a04c33
--- /dev/null
+++ b/common/modules/relation/models/RelationQuery.php
@@ -0,0 +1,35 @@
+andWhere('[[status]]=1');
+ return $this;
+ }*/
+
+ /**
+ * @inheritdoc
+ * @return Relation[]|array
+ */
+ public function all($db = null)
+ {
+ return parent::all($db);
+ }
+
+ /**
+ * @inheritdoc
+ * @return Relation|array|null
+ */
+ public function one($db = null)
+ {
+ return parent::one($db);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/relation/relationBehavior.php b/common/modules/relation/relationBehavior.php
new file mode 100644
index 0000000..3f83836
--- /dev/null
+++ b/common/modules/relation/relationBehavior.php
@@ -0,0 +1,224 @@
+relations as $relation_key => &$relation) {
+ if (is_string($relation)) {
+ // Get data from module's data
+ $relation_entity = $relation;
+ $relation = $this->_getRelationParams($relation_key);
+ $relation['inner'] = $relation[$relation_entity];
+ $relation['outer'] = $relation[$relation_entity == 'entity1' ? 'entity2' : 'entity1'];
+ $relation['linked_table'] = $relation['via']['model']::tableName();
+ }
+ $this->fields[$relation['field']] = $relation_key;
+ }
+ }
+
+ /*
+ * Events for auto-drive relations data
+ */
+ public function events()
+ {
+ return [
+ ActiveRecord::EVENT_AFTER_INSERT => 'relationsAfterSave',
+ ActiveRecord::EVENT_AFTER_UPDATE => 'relationsAfterSave',
+ ActiveRecord::EVENT_BEFORE_DELETE => 'relationBeforeDelete',
+ ];
+ }
+
+ public function relationsAfterSave($insert) {
+ if (is_array($modelPrimaryKey = $this->owner->getPrimaryKey())) {
+ throw new ErrorException('This behavior does not support composite primary keys');
+ }
+ foreach ($this->relations as $relation_key => $relation) {
+ if (empty($relation['field']))
+ continue;
+ $values = $this->{$relation['field']};
+
+ /** @var ActiveRecord $model */
+ $model = new $relation['inner']['model'];
+
+ $connection = $model::getDb();
+ $transaction = $connection->beginTransaction();
+
+ // @todo Refix to ActiveRecord format
+ try {
+ // Delete all links from viaTable
+ $connection->createCommand()
+ ->delete
+ (
+ $relation['linked_table'],
+ [$relation['inner']['linked_key'] => $this->owner->{$relation['inner']['key']}]
+ )
+ ->execute();
+
+ if (!empty($values)) {
+ foreach($values as $value) {
+ $connection->createCommand()
+ ->insert
+ (
+ $relation['linked_table'],
+ [
+ $relation['inner']['linked_key'] => $this->owner->{$relation['inner']['key']},
+ $relation['outer']['linked_key'] => $value
+ ]
+ )
+ ->execute();
+ }
+ }
+ $transaction->commit();
+
+// $model->link($relation_key, )
+ } catch (Exception $ex) {
+ var_dump($relation_key, $relation);exit;
+ $transaction->rollback();
+ throw $ex;
+ }
+ }
+ }
+
+ public function relationBeforeDelete() {
+ if (is_array($modelPrimaryKey = $this->owner->getPrimaryKey())) {
+ throw new ErrorException('This behavior does not support composite primary keys');
+ }
+ foreach ($this->relations as $relation_key => $relation) {
+ if (empty($relation['field']))
+ continue;
+ $values = $this->{$relation['field']};
+
+ /** @var ActiveRecord $model */
+ $model = new $relation['inner']['model'];
+
+ $connection = $model::getDb();
+ $transaction = $connection->beginTransaction();
+
+ // @todo Refix to ActiveRecord format
+ try {
+ // Delete all links from viaTable
+ $connection->createCommand()
+ ->delete
+ (
+ $relation['linked_table'],
+ [$relation['inner']['linked_key'] => $this->owner->{$relation['inner']['key']}]
+ )
+ ->execute();
+ $transaction->commit();
+ } catch (Exception $ex) {
+ $transaction->rollback();
+ throw $ex;
+ }
+ }
+ }
+
+ /**
+ * Get related data for $relation
+ * @params string $relation Relation key
+ */
+ public function getRelations($relation) {
+ $relation = $this->_getRelation($relation);
+ return
+ $this->owner
+ ->hasMany($relation['outer']['model'], [$relation['outer']['key'] => $relation['outer']['linked_key']])
+ ->viaTable($relation['linked_table'], [$relation['inner']['linked_key'] => $relation['inner']['key']]);
+ }
+
+ /*
+ * Get relation params for $relation
+ * @param string $relation Relation key
+ */
+ protected function _getRelation($relation) {
+ $relation = strtolower($relation);
+ return isset($this->relations[$relation]) ? $this->relations[$relation] : null;
+ }
+
+ /**
+ * Return relation data from main app config
+ * @params string $section Relations key
+ */
+ protected function _getRelationParams($section) {
+ $relation = relationHelper::getRelation($section);
+ if (!$relation)
+ throw new Exception('Relation "' . $section . '" not set on this application.');
+ return $relation;
+ }
+
+ protected function _getRelationNameByField($field) {
+ return isset($this->fields[$field]) ? $this->fields[$field] : null;
+ }
+
+ protected function _getRelationByField($field) {
+ return ( isset($this->fields[$field]) && isset($this->relations[$this->fields[$field]]) ) ? $this->relations[$this->fields[$field]] : null;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function canGetProperty($name, $checkVars = true)
+ {
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function canSetProperty($name, $checkVars = true)
+ {
+ return array_key_exists($name, $this->fields) ?
+ true : parent::canSetProperty($name, $checkVars = true);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function __set($name, $value) {
+ if (isset($this->fields[$name])) {
+ $this->values[$name] = $value;
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function __get($name) {
+ if (isset($this->values[$name])) {
+ return $this->values[$name];
+ } else {
+ $relation_key = $this->_getRelationNameByField($name);
+ if (!$relation_key)
+ return;
+
+ return $this->getRelations($relation_key);
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/modules/relation/relationHelper.php b/common/modules/relation/relationHelper.php
new file mode 100644
index 0000000..bdb6991
--- /dev/null
+++ b/common/modules/relation/relationHelper.php
@@ -0,0 +1,60 @@
+getModule('relation');
+
+ if (!is_array($module->relations))
+ return [];
+
+ return $module->relations;
+ }
+
+ /**
+ * Return one relation for key $name
+ * @param string $name
+ * @return string (@todo refix to relationOject)
+ */
+ public static function getRelation($name) {
+ $name = strtolower($name);
+ if (isset(self::$relations[$name])) {
+ return self::$relations[$name];
+ }
+ $relations = self::getRelations();
+ if (isset($relations[$name])) {
+ self::$relations[$name] = self::prepareRelation($relations[$name]);
+ } else {
+ self::$relations[$name] = null;
+ }
+ return self::$relations[$name];
+ }
+
+
+ private static function prepareRelation($relation) {
+ if (!isset($relation['linked_table']) && isset($relation['via']['model'])) {
+ $relation['linked_table'] = $relation['via']['model']::tableName();
+ }
+ return $relation;
+ }
+
+ /**
+ * @param string $name
+ * @return bool
+ */
+ public static function issetRelation($name) {
+ $relations = self::getRelations();
+ return isset($relations[$name]);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/relation/relationObject.php b/common/modules/relation/relationObject.php
new file mode 100644
index 0000000..9dce6fa
--- /dev/null
+++ b/common/modules/relation/relationObject.php
@@ -0,0 +1,29 @@
+_getRelationParams($relation_key);
+ $relation['inner'] = $relation[$relation_entity];
+ $relation['outer'] = $relation[$relation_entity == 'entity1' ? 'entity2' : 'entity1'];
+ $relation['linked_table'] = $relation['via']['model']::tableName();
+ }
+
+ $this->moduleRelations = relationHelper::getRelations();
+
+
+ }
+}
\ No newline at end of file
diff --git a/common/modules/relation/relationQueryTrait.php b/common/modules/relation/relationQueryTrait.php
new file mode 100644
index 0000000..822556c
--- /dev/null
+++ b/common/modules/relation/relationQueryTrait.php
@@ -0,0 +1,27 @@
+modelClass;
+ self::$model = new $class;
+ }
+ return self::$model;
+ }
+
+ public function getRelations($relationKey) {
+
+ }
+}
\ No newline at end of file
diff --git a/common/modules/relation/views/default/index.php b/common/modules/relation/views/default/index.php
new file mode 100644
index 0000000..e8f0a30
--- /dev/null
+++ b/common/modules/relation/views/default/index.php
@@ -0,0 +1,12 @@
+
+
= $this->context->action->uniqueId ?>
+
+ This is the view content for action "= $this->context->action->id ?>".
+ The action belongs to the controller "= get_class($this->context) ?>"
+ in the "= $this->context->module->id ?>" module.
+
+
+ You may customize this page by editing the following file:
+ = __FILE__ ?>
+
+
diff --git a/common/modules/relation/views/manage/_form.php b/common/modules/relation/views/manage/_form.php
new file mode 100644
index 0000000..0c047f0
--- /dev/null
+++ b/common/modules/relation/views/manage/_form.php
@@ -0,0 +1,36 @@
+
+
+
diff --git a/common/modules/relation/views/manage/create.php b/common/modules/relation/views/manage/create.php
new file mode 100644
index 0000000..743430c
--- /dev/null
+++ b/common/modules/relation/views/manage/create.php
@@ -0,0 +1,25 @@
+title = Yii::t('relation', $relation['name']);
+$this->params['breadcrumbs'][] = ['label' => Yii::t('relation', 'Relations'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('relation', $relation['name']), 'url' => ['/relation/manage/pars', 'relation' => $relation_key]];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'items1' => $items1,
+ 'items2' => $items2,
+ 'relation_key' => $relation_key,
+ 'relation' => $relation,
+ ]) ?>
+
+
diff --git a/common/modules/relation/views/manage/pars.php b/common/modules/relation/views/manage/pars.php
new file mode 100644
index 0000000..c82accf
--- /dev/null
+++ b/common/modules/relation/views/manage/pars.php
@@ -0,0 +1,54 @@
+title = Yii::t('relation', 'Relation items for {relation}', ['relation' => $relation['name']]);
+$this->params['breadcrumbs'][] = ['label' => Yii::t('relation', 'Relations'), 'url' => ['/relation/manage']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('relation', 'Create relation'), ['create?relation='. $relation_key], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ 'alias',
+ 'entity1.'. $relation['entity1']['listField'],
+ 'entity2.'. $relation['entity2']['listField'],
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{update} {delete}',
+ 'buttons' => [
+ 'update' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('relation', 'Edit par'),
+ ]);
+ },
+ 'delete' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('relation', 'Delete par'),
+ ]);
+ },
+ ],
+ 'urlCreator' => function ($action, $model, $key, $index) use ($relation, $relation_key) {
+ if ($action === 'update') {
+ $url ='/admin/relation/manage/update?relation='. $relation_key .'&id='. $model->{$relation['entity1']['linked_key']} .':'. $model->{$relation['entity2']['linked_key']};
+ return $url;
+ }
+ if ($action === 'delete') {
+ $url ='/admin/relation/manage/delete?relation='. $relation_key .'&id='. $model->{$relation['entity1']['linked_key']} .':'. $model->{$relation['entity2']['linked_key']};
+ return $url;
+ }
+ }
+ ],
+ ],
+ ]); ?>
+
diff --git a/common/modules/relation/views/manage/relations.php b/common/modules/relation/views/manage/relations.php
new file mode 100644
index 0000000..46950ab
--- /dev/null
+++ b/common/modules/relation/views/manage/relations.php
@@ -0,0 +1,53 @@
+title = Yii::t('relation', 'Relations');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ $relations,
+ 'sort' => [
+ 'attributes' => ['name', 'key', 'entity1_label', 'entity2_label'],
+ ],
+ 'pagination' => [
+ 'pageSize' => 10,
+ ],
+ ]);
+ ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ 'name',
+ 'key',
+ 'entity1_label',
+ 'entity2_label',
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{view}',
+ 'buttons' => [
+ 'view' => function ($url, $model) {
+ return Html::a(' ', $url, [
+ 'title' => Yii::t('relation', 'View pars'),
+ ]);
+ },
+ ],
+ 'urlCreator' => function ($action, $model) {
+ if ($action === 'view') {
+ $url ='/admin/relation/manage/pars?relation='. $model['key'];
+ return $url;
+ }
+ }
+ ],
+ ],
+ ]); ?>
+
diff --git a/common/modules/relation/views/manage/update.php b/common/modules/relation/views/manage/update.php
new file mode 100644
index 0000000..743430c
--- /dev/null
+++ b/common/modules/relation/views/manage/update.php
@@ -0,0 +1,25 @@
+title = Yii::t('relation', $relation['name']);
+$this->params['breadcrumbs'][] = ['label' => Yii::t('relation', 'Relations'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('relation', $relation['name']), 'url' => ['/relation/manage/pars', 'relation' => $relation_key]];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'items1' => $items1,
+ 'items2' => $items2,
+ 'relation_key' => $relation_key,
+ 'relation' => $relation,
+ ]) ?>
+
+
diff --git a/common/modules/rubrication/Module.php b/common/modules/rubrication/Module.php
new file mode 100644
index 0000000..ffc888c
--- /dev/null
+++ b/common/modules/rubrication/Module.php
@@ -0,0 +1,28 @@
+render('index');
+ }
+}
diff --git a/common/modules/rubrication/controllers/TaxGroupController.php b/common/modules/rubrication/controllers/TaxGroupController.php
new file mode 100644
index 0000000..f112d9c
--- /dev/null
+++ b/common/modules/rubrication/controllers/TaxGroupController.php
@@ -0,0 +1,152 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all TaxGroup models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $dataProvider = new ActiveDataProvider([
+ 'query' => TaxGroup::find(),
+ ]);
+
+ return $this->render('index', [
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single TaxGroup model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new TaxGroup model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new TaxGroup();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->tax_group_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing TaxGroup 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->tax_group_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing TaxGroup 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']);
+ }
+
+ /*
+ * Group-relations
+ */
+ public function actionRelation($id, $relations = ['tax_option_to_group', 'tax_option_to_option']) {
+ $group = $this->findModel($id);
+ $items = [];
+
+ foreach($relations as $relation) {
+ $rows = $group->getRelations($relation)->all();
+ $items = array_merge($items, $rows);
+ }
+
+ return $this->render('relations', [
+ 'items' => $items,
+ 'group' => $group,
+ ]);
+ }
+
+ /*
+ * Rebuilp MP-params for group options
+ */
+ public function actionRebuild($id) {
+ TaxOption::find()->rebuildMP($id);
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the TaxGroup model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return TaxGroup the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = TaxGroup::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/rubrication/controllers/TaxOptionController.php b/common/modules/rubrication/controllers/TaxOptionController.php
new file mode 100644
index 0000000..3ca7b29
--- /dev/null
+++ b/common/modules/rubrication/controllers/TaxOptionController.php
@@ -0,0 +1,171 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all TaxOption models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new TaxOptionSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ $group = TaxGroup::findOne(Yii::$app->request->queryParams['group']);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ 'group' => $group,
+ ]);
+ }
+
+ /**
+ * Displays a single TaxOption model.
+ * @param string $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ $model = $this->findModel($id);
+ $group = TaxGroup::findOne($model->tax_group_id);
+ return $this->render('view', [
+ 'model' => $model,
+ 'group' => $group,
+ ]);
+ }
+
+ /**
+ * Creates a new TaxOption model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new TaxOption();
+ $group = TaxGroup::findOne(Yii::$app->request->queryParams['group']);
+ $valueModelName = $this->getValueModelName($group);
+ $valueModel = new $valueModelName;
+
+ if ($model->load(Yii::$app->request->post()) && $valueModel->load(Yii::$app->request->post())) {
+ $model->save();
+
+ $valueModel->tax_option_id = $model->tax_option_id;
+ $valueModel->save();
+
+ $model->default_value = $valueModel->tax_value_id;
+ $model->save();
+
+ return is_null(Yii::$app->request->post('create_and_new')) ? $this->redirect(['view', 'id' => $model->tax_option_id]) : $this->redirect(array_merge(['create'], Yii::$app->request->queryParams));
+ } else {
+ $model->tax_group_id = $group->tax_group_id;
+ if (!empty(Yii::$app->request->queryParams['parent'])) {
+ $model->parent_id = Yii::$app->request->queryParams['parent'];
+ }
+ return $this->render('create', [
+ 'model' => $model,
+ 'group' => $group,
+ 'valueModel' => $valueModel,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing TaxOption model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param string $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+ $group = TaxGroup::findOne($model->tax_group_id);
+ $valueModelName = $this->getValueModelName($group);
+ $valueModel = $valueModelName::findOne($model->default_value);
+
+ if ($model->load(Yii::$app->request->post()) && $valueModel->load(Yii::$app->request->post())) {
+ $model->save();
+ $valueModel->tax_option_id = $model->tax_option_id;
+ $valueModel->save();
+
+ $model->default_value = $valueModel->tax_value_id;
+ $model->save();
+
+ TaxOption::find()->rebuildMP($model->tax_group_id);
+
+ return $this->redirect(['view', 'id' => $model->tax_option_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ 'group' => $group,
+ 'valueModel' => $valueModel,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing TaxOption model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param string $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $model = $this->findModel($id);
+ $group_id = $model->tax_group_id;
+
+ $model->delete();
+
+ return $this->redirect(['index', 'group' => $group_id]);
+ }
+
+ /**
+ * Finds the TaxOption model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param string $id
+ * @return TaxOption the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = TaxOption::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+ protected function getValueModelName($group) {
+ $valueClass = '\common\modules\rubrication\models\TaxValue'. ucfirst($group->module);
+ return class_exists($valueClass) ? $valueClass : FALSE;
+ }
+}
diff --git a/common/modules/rubrication/helpers/RubricationHelper.php b/common/modules/rubrication/helpers/RubricationHelper.php
new file mode 100644
index 0000000..39e7cbb
--- /dev/null
+++ b/common/modules/rubrication/helpers/RubricationHelper.php
@@ -0,0 +1,43 @@
+ Yii::t('order', 'Invisible'),
+ 1 => Yii::t('order', 'Active'),
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ * Returns sort-interval of appropriate options and others
+ * @return array.
+ */
+ static public function SortArray($low = 0, $high = 100) {
+ return range($low, $high);
+ }
+
+ static public function OptionTypes() {
+ if (!is_null(self::$types)) {
+ return self::$types;
+ }
+
+ $module = \Yii::$app->getModule('rubrication');
+
+ if (!is_array($module->types))
+ return [];
+
+ return $module->types;
+
+ }
+}
\ No newline at end of file
diff --git a/common/modules/rubrication/models/TaxGroup.php b/common/modules/rubrication/models/TaxGroup.php
new file mode 100644
index 0000000..d9df989
--- /dev/null
+++ b/common/modules/rubrication/models/TaxGroup.php
@@ -0,0 +1,116 @@
+ relationBehavior::className(),
+ 'relations' => [
+// 'tax_option_to_group' => 'entity2',
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return '{{%tax_group}}';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['alias', 'name', 'module'], 'required'],
+ [['description', 'settings'], 'string'],
+ [['hierarchical'], 'boolean'],
+ [['alias', 'module'], 'string', 'max' => 50],
+ [['name'], 'string', 'max' => 255],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_group_id' => 'Tax Group ID',
+ 'alias' => 'Alias',
+ 'name' => 'Name',
+ 'description' => 'Description',
+ 'module' => 'Module',
+ 'hierarchical' => 'Hierarchical',
+// 'settings' => 'Settings',
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroupToGroups()
+ {
+ return $this->hasMany(TaxGroupToGroup::className(), ['tax_group1_id' => 'tax_group_id'])->inverseOf('taxGroup1');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroupToGroups0()
+ {
+ return $this->hasMany(TaxGroupToGroup::className(), ['tax_group2_id' => 'tax_group_id'])->inverseOf('taxGroup2');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptions()
+ {
+ return $this->hasMany(TaxOption::className(), ['tax_group_id' => 'tax_group_id'])->inverseOf('taxGroup');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptionToGroups()
+ {
+ return $this->hasMany(TaxOptionToGroup::className(), ['tax_group_id' => 'tax_group_id'])->inverseOf('taxGroup');
+ }
+
+ public function getValueModelName($full_path = true) {
+ $valueClass = 'TaxValue'. ucfirst($this->module);
+ $fullClass = '\common\modules\rubrication\models\\'. $valueClass;
+ return class_exists($fullClass) ? $full_path ? $fullClass : $valueClass : FALSE;
+ }
+}
diff --git a/common/modules/rubrication/models/TaxGroupToGroup.php b/common/modules/rubrication/models/TaxGroupToGroup.php
new file mode 100644
index 0000000..6d3fe73
--- /dev/null
+++ b/common/modules/rubrication/models/TaxGroupToGroup.php
@@ -0,0 +1,69 @@
+ 50]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_group1_id' => Yii::t('app', 'Tax Group1 ID'),
+ 'tax_group2_id' => Yii::t('app', 'Tax Group2 ID'),
+ 'alias' => Yii::t('app', 'Alias'),
+ 'sort' => Yii::t('app', 'Sort'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroup1()
+ {
+ return $this->hasOne(TaxGroup::className(), ['tax_group_id' => 'tax_group1_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroup2()
+ {
+ return $this->hasOne(TaxGroup::className(), ['tax_group_id' => 'tax_group2_id']);
+ }
+}
diff --git a/common/modules/rubrication/models/TaxOption.php b/common/modules/rubrication/models/TaxOption.php
new file mode 100644
index 0000000..d25262b
--- /dev/null
+++ b/common/modules/rubrication/models/TaxOption.php
@@ -0,0 +1,232 @@
+ [
+ 'class' => ArtboxTreeBehavior::className(),
+ 'keyNameGroup' => 'tax_group_id',
+ ],
+ 'slug' => [
+ 'class' => 'common\behaviors\Slug',
+ 'in_attribute' => 'ValueRenderFlash',
+ 'out_attribute' => 'alias',
+ 'translit' => true
+ ],
+ ];
+ }
+
+ public function events() {
+ return $this->events();
+ return [
+// ActiveRecord::EVENT_BEFORE_DELETE => 'beforeDelete',
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return '{{%tax_option}}';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['tax_group_id'], 'required'],
+ [['tax_group_id', 'parent_id', 'sort', 'default_value'], 'integer'],
+ [['alias'], 'string', 'max' => 50],
+ [['tax_group_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxGroup::className(), 'targetAttribute' => ['tax_group_id' => 'tax_group_id']],
+// [['parent_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['parent_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_option_id' => Yii::t('app', 'Tax Option ID'),
+ 'tax_group_id' => Yii::t('app', 'Tax Group ID'),
+ 'parent_id' => Yii::t('app', 'Parent ID'),
+ 'alias' => Yii::t('app', 'Alias'),
+ 'sort' => Yii::t('app', 'Sort'),
+ 'default_value' => Yii::t('app', 'Default Value'),
+ ];
+ }
+
+ public static function find() {
+ return new TaxOptionQuery(get_called_class());
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxEntityRelations()
+ {
+ return $this->hasMany(TaxEntityRelation::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxOption');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getGroup()
+ {
+ return $this->getTaxGroup();
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroup()
+ {
+ return $this->hasOne(TaxGroup::className(), ['tax_group_id' => 'tax_group_id'])->inverseOf('taxOptions');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptions()
+ {
+ return $this->hasMany(TaxOption::className(), ['parent_id' => 'tax_option_id'])->inverseOf('parent');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptionToGroups()
+ {
+ return $this->hasMany(TaxOptionToGroup::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxOption');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptionToOptions()
+ {
+ return $this->hasMany(TaxOptionToOption::className(), ['tax_option1_id' => 'tax_option_id'])->inverseOf('taxOption1');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptionToOptions0()
+ {
+ return $this->hasMany(TaxOptionToOption::className(), ['tax_option2_id' => 'tax_option_id'])->inverseOf('taxOption2');
+ }
+
+ /**
+ */
+ public function getValue()
+ {
+ $valueClass = $this->getValueModelName();
+ if ($valueClass) {
+ return $this->hasOne($valueClass, ['tax_value_id' => 'default_value', 'tax_option_id' => 'tax_option_id'])->inverseOf('taxOption');
+ }
+ }
+
+ /**
+ */
+ public function getValueRenderFlash()
+ {
+ $valueClass = $this->getValueModelName();
+ $value = $this->getValue()->one();
+ if ($valueClass && method_exists($valueClass, 'getValueRenderFlash')) {
+ return $valueClass::getValueRenderFlash($value);
+ } elseif(!empty($value)) {
+ return $value->value;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ */
+ public function getValueRenderHTML()
+ {
+ $valueClass = $this->getValueModelName();
+ $value = $this->getValue()->one();
+ if ($valueClass && method_exists($valueClass, 'getValueRenderHTML')) {
+ return $valueClass::getValueRenderHTML($value);
+ } else {
+ return $value->value;
+ }
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getValues()
+ {
+ if ($valueClass = $this->getValueModelName())
+ return $this->hasMany($valueClass, ['tax_option_id' => 'tax_option_id'])->inverseOf('taxOption')->cascadeOnDelete();
+ }
+
+ public function beforeSave($insert)
+ {
+ if (parent::beforeSave($insert)) {
+
+ if (empty($this->parent_id))
+ $this->parent_id = 0;
+
+
+
+ return true;
+ }
+ return false;
+ }
+
+// public function beforeDelete() {
+// if ( ($model = $this->getValueModelName()) !== FALSE ) {
+//
+// }
+// }
+
+ private function getValueModelName() {
+ $group = $this->getTaxGroup()->one();
+ $valueClass = '\common\modules\rubrication\models\TaxValue'. ucfirst($group->module);
+ return class_exists($valueClass) ? $valueClass : FALSE;
+ }
+}
diff --git a/common/modules/rubrication/models/TaxOptionQuery.php b/common/modules/rubrication/models/TaxOptionQuery.php
new file mode 100644
index 0000000..5113f13
--- /dev/null
+++ b/common/modules/rubrication/models/TaxOptionQuery.php
@@ -0,0 +1,37 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @inheritdoc
+ * @return TaxOption[]|array
+ */
+ public function all($db = null)
+ {
+ return parent::all($db);
+ }
+
+ /**
+ * @inheritdoc
+ * @return TaxOption|array|null
+ */
+ public function one($db = null)
+ {
+ return parent::one($db);
+ }
+}
diff --git a/common/modules/rubrication/models/TaxOptionRelation.php b/common/modules/rubrication/models/TaxOptionRelation.php
new file mode 100644
index 0000000..f80a7f1
--- /dev/null
+++ b/common/modules/rubrication/models/TaxOptionRelation.php
@@ -0,0 +1,50 @@
+ 50]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_option1_id' => Yii::t('product', 'Tax Option1 ID'),
+ 'tax_option2_id' => Yii::t('product', 'Tax Option2 ID'),
+ 'alias' => Yii::t('product', 'Alias'),
+ 'sort' => Yii::t('product', 'Sort'),
+ ];
+ }
+}
diff --git a/common/modules/rubrication/models/TaxOptionSearch.php b/common/modules/rubrication/models/TaxOptionSearch.php
new file mode 100644
index 0000000..adfb6bc
--- /dev/null
+++ b/common/modules/rubrication/models/TaxOptionSearch.php
@@ -0,0 +1,85 @@
+tax_group_id = intval($params['group']);
+// $group = TaxGroup::findOne($this->tax_group_id);
+ unset($params['group']);
+ }
+// if (!empty($params['value_render_flat'])) {
+// $this->value_render_flat = trim($params['value_render_flat']);
+// }
+ $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;
+ }
+
+// if (!empty($group)) {
+// $query->joinWith('Value');
+// }
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'tax_option_id' => $this->tax_option_id,
+ 'tax_group_id' => $this->tax_group_id,
+ 'parent_id' => $this->parent_id,
+ 'sort' => $this->sort,
+ ]);
+
+ $query->andFilterWhere(['like', 'alias', $this->alias]);
+ $query->andFilterWhere(['like', 'tax_value_string.value', $this->default_value]);
+
+ $query->orderBy(['path_int' => SORT_ASC, 'depth' => SORT_ASC, 'sort' => SORT_ASC]);
+
+ return $dataProvider;
+ }
+}
diff --git a/common/modules/rubrication/models/TaxValueInt.php b/common/modules/rubrication/models/TaxValueInt.php
new file mode 100644
index 0000000..1fa9ea2
--- /dev/null
+++ b/common/modules/rubrication/models/TaxValueInt.php
@@ -0,0 +1,57 @@
+ true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['tax_option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_value_id' => Yii::t('app', 'Tax Value ID'),
+ 'tax_option_id' => Yii::t('app', 'Tax Option ID'),
+ 'value' => Yii::t('app', 'Value'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxValues');
+ }
+}
diff --git a/common/modules/rubrication/models/TaxValueLink.php b/common/modules/rubrication/models/TaxValueLink.php
new file mode 100644
index 0000000..f2eb859
--- /dev/null
+++ b/common/modules/rubrication/models/TaxValueLink.php
@@ -0,0 +1,69 @@
+ 150],
+ [['link'], 'string', 'max' => 255],
+ [['tax_option_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['tax_option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_value_id' => Yii::t('app', 'Tax Value ID'),
+ 'tax_option_id' => Yii::t('app', 'Tax Option ID'),
+ 'name' => Yii::t('app', 'Name'),
+ 'link' => Yii::t('app', 'Link'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxValueLinks');
+ }
+
+ public static function getValueRenderFlash($value) {
+ return $value->name;
+ }
+
+ public static function getValueRenderHTML($value) {
+ return Html::a($value->name, $value->link);
+ }
+}
diff --git a/common/modules/rubrication/models/TaxValueString.php b/common/modules/rubrication/models/TaxValueString.php
new file mode 100644
index 0000000..d9f617a
--- /dev/null
+++ b/common/modules/rubrication/models/TaxValueString.php
@@ -0,0 +1,58 @@
+ 255],
+ [['tax_option_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['tax_option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_value_id' => Yii::t('rubrication', 'Tax Value ID'),
+ 'tax_option_id' => Yii::t('rubrication', 'Tax Option ID'),
+ 'value' => Yii::t('rubrication', 'Value'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxValueStrings');
+ }
+}
diff --git a/common/modules/rubrication/views/default/index.php b/common/modules/rubrication/views/default/index.php
new file mode 100644
index 0000000..6b05acc
--- /dev/null
+++ b/common/modules/rubrication/views/default/index.php
@@ -0,0 +1,12 @@
+
+
= $this->context->action->uniqueId ?>
+
+ This is the view content for action "= $this->context->action->id ?>".
+ The action belongs to the controller "= get_class($this->context) ?>"
+ in the "= $this->context->module->id ?>" module.
+
+
+ You may customize this page by editing the following file:
+ = __FILE__ ?>
+
+
diff --git a/common/modules/rubrication/views/tax-group/_form.php b/common/modules/rubrication/views/tax-group/_form.php
new file mode 100644
index 0000000..b593821
--- /dev/null
+++ b/common/modules/rubrication/views/tax-group/_form.php
@@ -0,0 +1,34 @@
+
+
+
diff --git a/common/modules/rubrication/views/tax-group/create.php b/common/modules/rubrication/views/tax-group/create.php
new file mode 100644
index 0000000..e671dd1
--- /dev/null
+++ b/common/modules/rubrication/views/tax-group/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('rubrication', 'Create Tax Group');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', 'Tax Groups'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/rubrication/views/tax-group/index.php b/common/modules/rubrication/views/tax-group/index.php
new file mode 100644
index 0000000..5e266ad
--- /dev/null
+++ b/common/modules/rubrication/views/tax-group/index.php
@@ -0,0 +1,72 @@
+title = Yii::t('rubrication', 'Groups');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
+
= Html::encode($this->title) ?>
+
+ = Html::a(Yii::t('rubrication', 'Create Group'), ['create'], ['class' => 'btn btn-success']) ?>
+
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'name',
+ 'alias',
+ 'description:ntext',
+ 'module',
+ 'hierarchical:boolean',
+ // 'settings:ntext',
+
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{update} {options} {relations} {delete} {rebuild}',
+ 'buttons' => [
+ 'options' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('rubrication', 'Options'),
+ ]);
+ },
+ 'relations' => function ($url, $model) {
+ return Html::a('', $url, [
+ 'title' => Yii::t('rubrication', 'Relations'),
+ ]);
+ },
+ 'rebuild' => function ($url, $model) {
+ return Html::a('', $url, [
+ 'title' => Yii::t('rubrication', 'Rebuild cache'),
+ ]);
+ }
+ ],
+ 'urlCreator' => function ($action, $model, $key, $index) {
+ if ($action === 'options') {
+ $url ='/admin/rubrication/tax-option?group='.$model->tax_group_id;
+ return $url;
+ } elseif ($action === 'relations') {
+ $url ='/admin/rubrication/tax-group/relation&id='.$model->tax_group_id;
+ return $url;
+ } elseif ($action === 'update') {
+ $url ='/admin/rubrication/tax-group/update?id='.$model->tax_group_id;
+ return $url;
+ } elseif ($action === 'delete') {
+ $url ='/admin/rubrication/tax-group/delete?id='.$model->tax_group_id;
+ return $url;
+ } elseif ($action === 'rebuild') {
+ $url ='/admin/rubrication/tax-group/rebuild?id='.$model->tax_group_id;
+ return $url;
+ }
+ }
+ ],
+ ],
+ ]); ?>
+
+
diff --git a/common/modules/rubrication/views/tax-group/relations.php b/common/modules/rubrication/views/tax-group/relations.php
new file mode 100644
index 0000000..2c0c884
--- /dev/null
+++ b/common/modules/rubrication/views/tax-group/relations.php
@@ -0,0 +1,58 @@
+title = Yii::t('relation', 'Relation for Group "{group}"', ['group' => $group->name]);
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', 'Tax Groups'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+//= Html::a(Yii::t('relation', 'Create relation'), ['create?relation='. $relation_key], ['class' => 'btn btn-success']) ?>
+
+
+ $items,
+ 'sort' => [
+ 'attributes' => ['name', 'alias', 'entity1_label', 'entity2_label'],
+ ],
+ 'pagination' => [
+ 'pageSize' => 10,
+ ],
+ ]);
+ ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ 'name',
+ 'alias',
+ 'entity1_label',
+ 'entity2_label',
+ /*[
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{view}',
+ 'buttons' => [
+ 'view' => function ($url, $model) {
+ return Html::a(' ', $url, [
+ 'title' => Yii::t('relation', 'View pars'),
+ ]);
+ },
+ ],
+ 'urlCreator' => function ($action, $model, $key, $index) {
+ if ($action === 'view') {
+ $url ='/admin/relation/manage/pars?relation='. $model['key'];
+ return $url;
+ }
+ }
+ ],*/
+ ],
+ ]); ?>
+
diff --git a/common/modules/rubrication/views/tax-group/update.php b/common/modules/rubrication/views/tax-group/update.php
new file mode 100644
index 0000000..7f3feaa
--- /dev/null
+++ b/common/modules/rubrication/views/tax-group/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('rubrication', 'Update {modelClass}: ', [
+ 'modelClass' => 'Tax Group',
+]) . ' ' . $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', 'Groups'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->tax_group_id]];
+$this->params['breadcrumbs'][] = Yii::t('rubrication', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/rubrication/views/tax-group/view.php b/common/modules/rubrication/views/tax-group/view.php
new file mode 100644
index 0000000..3074048
--- /dev/null
+++ b/common/modules/rubrication/views/tax-group/view.php
@@ -0,0 +1,42 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', 'Tax Groups'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('rubrication', 'Update'), ['update', 'id' => $model->tax_group_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('rubrication', 'Delete'), ['delete', 'id' => $model->tax_group_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('rubrication', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+ = Html::a(Yii::t('rubrication', 'Create Option'), ['tax-option/create?group='. $model->tax_group_id], ['class' => 'btn btn-success']) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'tax_group_id',
+ 'alias',
+ 'name',
+ 'description:ntext',
+ 'module',
+ 'hierarchical:boolean',
+ 'settings:ntext',
+ ],
+ ]) ?>
+
+
diff --git a/common/modules/rubrication/views/tax-option/_form.php b/common/modules/rubrication/views/tax-option/_form.php
new file mode 100644
index 0000000..f29cdea
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/_form.php
@@ -0,0 +1,62 @@
+
+
+
diff --git a/common/modules/rubrication/views/tax-option/_search.php b/common/modules/rubrication/views/tax-option/_search.php
new file mode 100644
index 0000000..145b33d
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/_search.php
@@ -0,0 +1,37 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'tax_option_id') ?>
+
+ = $form->field($model, 'tax_group_id') ?>
+
+ = $form->field($model, 'parent_id') ?>
+
+ = $form->field($model, 'alias') ?>
+
+ = $form->field($model, 'sort') ?>
+
+ field($model, 'default_value') ?>
+
+
+ = Html::submitButton(Yii::t('rubrication', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('rubrication', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/common/modules/rubrication/views/tax-option/create.php b/common/modules/rubrication/views/tax-option/create.php
new file mode 100644
index 0000000..71c91d6
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/create.php
@@ -0,0 +1,25 @@
+title = Yii::t('rubrication', 'Create Tax Option');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', 'Groups'), 'url' => ['tax-group/index']];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', $group->name), 'url' => ['index', 'group' => $group->tax_group_id]];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', Yii::t('rubrication', 'Options of {name}', ['name' => $group->name])), 'url' => ['index', 'group' => $group->tax_group_id]];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'group' => $group,
+ 'valueModel' => $valueModel,
+ ]) ?>
+
+
diff --git a/common/modules/rubrication/views/tax-option/index.php b/common/modules/rubrication/views/tax-option/index.php
new file mode 100644
index 0000000..986b02e
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/index.php
@@ -0,0 +1,137 @@
+title = Yii::t('rubrication', 'Options for group "{group}"', ['group' => $group->name]);
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', 'Groups'), 'url' => ['tax-group/index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('rubrication', 'Create Option'), ['create?group='. $group->tax_group_id], ['class' => 'btn btn-success']) ?>
+
+
+hierarchical) :?>
+ $dataProvider,
+// 'filterModel' => $searchModel,
+ 'columns' => [
+ [
+ 'label'=> Yii::t('rubrication', 'Value'),
+ 'content'=>function($data){
+ return str_repeat('-', $data->depth) .' '. $data->ValueRenderFlash;
+ }
+ ],
+ 'alias',
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{view} {update} {delete} {synonim}',
+ 'buttons' => [
+ 'synonim' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('rubrication', 'Synonims'),
+ ]);
+ },
+ ],
+ 'urlCreator' => function ($action, $model, $key, $index) {
+ if ($action === 'view') {
+ $url = '/admin/rubrication/tax-option/view?id=' . $model->tax_option_id;
+ return $url;
+ } elseif ($action === 'update') {
+ $url ='/admin/rubrication/tax-option/update?id='.$model->tax_option_id;
+ return $url;
+ } elseif ($action === 'delete') {
+ $url ='/admin/rubrication/tax-option/delete?id='.$model->tax_option_id;
+ return $url;
+ } elseif ($action === 'synonim') {
+ $url ='/admin/rubrication/tax-synonim/delete?id='.$model->tax_option_id;
+ return $url;
+ }
+ }
+ ],
+ ],
+ ]);?>
+ $dataProvider,
+ 'keyNameId' => 'tax_option_id',
+ 'keyNameParentId' => 'parent_id',
+ 'rootParentId' => 0,
+ 'columns' => [
+ [
+ 'attribute' => 'valueRenderHTML',
+ 'label' => Yii::t('rubrication', 'Value'),
+ 'format' => 'html'
+ ],
+ 'alias',
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{update} {delete} {synonim}',
+ 'buttons' => [
+ 'synonim' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('rubrication', 'Synonims'),
+ ]);
+ },
+ ],
+ 'urlCreator' => function ($action, $model, $key, $index) {
+ if ($action === 'update') {
+ $url ='/admin/rubrication/tax-option/update?id='.$model->tax_option_id;
+ return $url;
+ } elseif ($action === 'delete') {
+ $url ='/admin/rubrication/tax-option/delete?id='.$model->tax_option_id;
+ return $url;
+ } elseif ($action === 'synonim') {
+ $url ='/admin/rubrication/tax-synonim/delete?id='.$model->tax_option_id;
+ return $url;
+ }
+ }
+ ],
+ ]
+ ]); */?>
+
+ $dataProvider,
+// 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ 'valueRenderHTML',
+ 'alias',
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{update} {delete} {synonim}',
+ 'buttons' => [
+ 'synonim' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('rubrication', 'Synonims'),
+ ]);
+ },
+ ],
+ 'urlCreator' => function ($action, $model, $key, $index) {
+ if ($action === 'update') {
+ $url ='/admin/rubrication/tax-option/update?id='.$model->tax_option_id;
+ return $url;
+ } elseif ($action === 'delete') {
+ $url ='/admin/rubrication/tax-option/delete?id='.$model->tax_option_id;
+ return $url;
+ } elseif ($action === 'synonim') {
+ $url ='/admin/rubrication/tax-synonim/delete?id='.$model->tax_option_id;
+ return $url;
+ }
+ }
+ ],
+ ],
+ ]);?>
+
+
diff --git a/common/modules/rubrication/views/tax-option/update.php b/common/modules/rubrication/views/tax-option/update.php
new file mode 100644
index 0000000..5c7164f
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/update.php
@@ -0,0 +1,26 @@
+title = Yii::t('rubrication', 'Update {modelClass}: ', [
+ 'modelClass' => 'Tax Option',
+]) . ' ' . $model->tax_option_id;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', 'Groups'), 'url' => ['tax-group/index']];
+$this->params['breadcrumbs'][] = ['label' => $group->name, 'url' => ['view', 'id' => $group->tax_group_id]];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', Yii::t('rubrication', 'Options of {name}', ['name' => $group->name])), 'url' => ['index', 'group' => $group->tax_group_id]];
+$this->params['breadcrumbs'][] = Yii::t('rubrication', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'group' => $group,
+ 'valueModel' => $valueModel,
+ ]) ?>
+
+
diff --git a/common/modules/rubrication/views/tax-option/value/_fields_int.php b/common/modules/rubrication/views/tax-option/value/_fields_int.php
new file mode 100644
index 0000000..6a35b2f
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/value/_fields_int.php
@@ -0,0 +1 @@
+= $form->field($valueModel, 'value')->textInput() ?>
\ No newline at end of file
diff --git a/common/modules/rubrication/views/tax-option/value/_fields_link.php b/common/modules/rubrication/views/tax-option/value/_fields_link.php
new file mode 100644
index 0000000..faebf0c
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/value/_fields_link.php
@@ -0,0 +1,2 @@
+= $form->field($valueModel, 'name')->textInput(['maxlength' => true]) ?>
+= $form->field($valueModel, 'link')->textInput(['maxlength' => true]) ?>
\ No newline at end of file
diff --git a/common/modules/rubrication/views/tax-option/value/_fields_string.php b/common/modules/rubrication/views/tax-option/value/_fields_string.php
new file mode 100644
index 0000000..6a35b2f
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/value/_fields_string.php
@@ -0,0 +1 @@
+= $form->field($valueModel, 'value')->textInput() ?>
\ No newline at end of file
diff --git a/common/modules/rubrication/views/tax-option/view.php b/common/modules/rubrication/views/tax-option/view.php
new file mode 100644
index 0000000..5cdc66e
--- /dev/null
+++ b/common/modules/rubrication/views/tax-option/view.php
@@ -0,0 +1,46 @@
+title = $model->valueRenderFlash;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', 'Groups'), 'url' => ['tax-group/index']];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', $group->name), 'url' => ['index', 'group' => $group->tax_group_id]];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('rubrication', Yii::t('rubrication', 'Options of {name}', ['name' => $group->name])), 'url' => ['index', 'group' => $group->tax_group_id]];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('rubrication', 'Update'), ['update', 'id' => $model->tax_option_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('rubrication', 'Delete'), ['delete', 'id' => $model->tax_option_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('rubrication', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+ = Html::a(Yii::t('rubrication', 'Create Option'), ['tax-option/create?group='. $model->tax_group_id], ['class' => 'btn btn-success']) ?>
+ parent_id)) :?>
+ = Html::a(Yii::t('rubrication', 'Create Option By {name}', ['name' => $model->parent->ValueRenderFlash]), ['tax-option/create?group='. $model->tax_group_id .'&parent='. $model->parent->tax_option_id], ['class' => 'btn btn-success']) ?>
+
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'tax_option_id',
+ 'ValueRenderFlash',
+ 'alias',
+ 'parent.ValueRenderFlash',
+ 'group.name',
+ 'sort',
+ ],
+ ]) ?>
+
+
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/FieldEditor.php b/common/widgets/FieldEditor.php
new file mode 100644
index 0000000..62261a4
--- /dev/null
+++ b/common/widgets/FieldEditor.php
@@ -0,0 +1,48 @@
+item_id && $this->model){
+ $widgetData = $this->findModel();
+ } else {
+ $widgetData= [new Fields()];
+ }
+
+ return $this->render($this->template.'_field',['model'=>ArrayHelper::toArray($widgetData)]);
+ }
+
+ protected function findModel()
+ {
+
+ if (($model = Fields::find()->where([
+ 'table_id'=>$this->item_id,
+ 'table_name'=>$this->model,
+ 'field_type'=>$this->template,
+ 'language'=>$this->language,
+ ])->all())) {
+
+ return $model;
+
+ } else {
+ return [new Fields()];
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/widgets/views/education_field.php b/common/widgets/views/education_field.php
new file mode 100644
index 0000000..ca6ad41
--- /dev/null
+++ b/common/widgets/views/education_field.php
@@ -0,0 +1,83 @@
+
+
+
+Образование
+
+
+ добавить еще
+
+
+
+
+
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..0090d0f
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,50 @@
+{
+ "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": "*",
+ "yiisoft/yii2-imagine": "^2.0",
+ "mihaildev/yii2-elfinder": "^1.1",
+ "kartik-v/yii2-widget-colorinput": "*",
+ "2amigos/yii2-transliterator-helper": "*",
+ "rmrevin/yii2-comments": "1.4.*",
+ "bower-asset/admin-lte": "*",
+ "FortAwesome/Font-Awesome": "*"
+ },
+ "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..e30a2eb
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,1969 @@
+{
+ "_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": "7543c4377d8fa28fcee5a5d9e3aabcb8",
+ "content-hash": "7c7bea2b7f00d81aab75adce39789723",
+ "packages": [
+ {
+ "name": "2amigos/yii2-transliterator-helper",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/2amigos/yii2-transliterator-helper.git",
+ "reference": "1e4284351f4250a8f2ce553ea4f420fcbb424309"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/2amigos/yii2-transliterator-helper/zipball/1e4284351f4250a8f2ce553ea4f420fcbb424309",
+ "reference": "1e4284351f4250a8f2ce553ea4f420fcbb424309",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2": "*"
+ },
+ "type": "yii2-extension",
+ "autoload": {
+ "psr-4": {
+ "dosamigos\\transliterator\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Antonio Ramirez",
+ "email": "ramirez.cobos@gmail.com",
+ "homepage": "http://www.ramirezcobos.com"
+ }
+ ],
+ "description": "Transliterator Helper for Yii2.",
+ "keywords": [
+ "extension",
+ "helper",
+ "transliterator",
+ "yii"
+ ],
+ "time": "2014-06-23 14:01:30"
+ },
+ {
+ "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/admin-lte",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/almasaeed2010/AdminLTE.git",
+ "reference": "fe147c9b2188bc3e4c651ca24581a6710d5421ff"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/almasaeed2010/AdminLTE/zipball/fe147c9b2188bc3e4c651ca24581a6710d5421ff",
+ "reference": "fe147c9b2188bc3e4c651ca24581a6710d5421ff",
+ "shasum": ""
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": [
+ "index2.html",
+ "dist/css/AdminLTE.css",
+ "dist/js/app.js",
+ "build/less/AdminLTE.less"
+ ],
+ "bower-asset-ignore": [
+ "/.*",
+ "node_modules",
+ "bower_components",
+ "composer.json",
+ "documentation"
+ ]
+ },
+ "license": [
+ "MIT"
+ ],
+ "description": "Admin dashboard and control panel template",
+ "keywords": [
+ "admin",
+ "backend",
+ "bootstrap",
+ "css",
+ "html",
+ "js",
+ "responsive",
+ "template",
+ "theme"
+ ],
+ "time": "2016-03-14 01:14:03"
+ },
+ {
+ "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.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jquery/jquery-dist.git",
+ "reference": "788eaba2f83e7b7445c7a83a50c81c0704423874"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/788eaba2f83e7b7445c7a83a50c81c0704423874",
+ "reference": "788eaba2f83e7b7445c7a83a50c81c0704423874",
+ "shasum": ""
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": "dist/jquery.js",
+ "bower-asset-ignore": [
+ "package.json"
+ ]
+ },
+ "license": [
+ "MIT"
+ ],
+ "keywords": [
+ "browser",
+ "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.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/RobinHerbots/jquery.inputmask.git",
+ "reference": "5a72c563b502b8e05958a524cdfffafe9987be38"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/5a72c563b502b8e05958a524cdfffafe9987be38",
+ "reference": "5a72c563b502b8e05958a524cdfffafe9987be38",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery": ">=1.7"
+ },
+ "type": "bower-asset-library",
+ "extra": {
+ "bower-asset-main": [
+ "./dist/inputmask/inputmask.js"
+ ],
+ "bower-asset-ignore": [
+ "**/*",
+ "!dist/*",
+ "!dist/inputmask/*",
+ "!dist/min/*",
+ "!dist/min/inputmask/*",
+ "!extra/bindings/*",
+ "!extra/dependencyLibs/*",
+ "!extra/phone-codes/*"
+ ]
+ },
+ "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.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/jquery-pjax.git",
+ "reference": "60728da6ade5879e807a49ce59ef9a72039b8978"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/60728da6ade5879e807a49ce59ef9a72039b8978",
+ "reference": "60728da6ade5879e807a49ce59ef9a72039b8978",
+ "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",
+ "CONTRIBUTING.md",
+ "vendor/",
+ "script/",
+ "test/"
+ ]
+ },
+ "license": [
+ "MIT"
+ ]
+ },
+ {
+ "name": "cebe/markdown",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cebe/markdown.git",
+ "reference": "e4499350d8a94c4c693a7e784295eff7a717ae67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cebe/markdown/zipball/e4499350d8a94c4c693a7e784295eff7a717ae67",
+ "reference": "e4499350d8a94c4c693a7e784295eff7a717ae67",
+ "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": "2016-02-09 22:09:46"
+ },
+ {
+ "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": "dev-master",
+ "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.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ezyang/htmlpurifier.git",
+ "reference": "ae1828d955112356f7677c465f94f7deb7d27a40"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40",
+ "reference": "ae1828d955112356f7677c465f94f7deb7d27a40",
+ "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": "2015-08-05 01:03:42"
+ },
+ {
+ "name": "fortawesome/font-awesome",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FortAwesome/Font-Awesome.git",
+ "reference": "03fd1951e930d4d4754204fdc532ca493e924a1b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FortAwesome/Font-Awesome/zipball/03fd1951e930d4d4754204fdc532ca493e924a1b",
+ "reference": "03fd1951e930d4d4754204fdc532ca493e924a1b",
+ "shasum": ""
+ },
+ "require-dev": {
+ "jekyll": "1.0.2",
+ "lessc": "1.4.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "OFL-1.1",
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Dave Gandy",
+ "email": "dave@fontawesome.io",
+ "homepage": "http://twitter.com/davegandy",
+ "role": "Developer"
+ }
+ ],
+ "description": "The iconic font and CSS framework",
+ "homepage": "http://fontawesome.io/",
+ "keywords": [
+ "FontAwesome",
+ "awesome",
+ "bootstrap",
+ "font",
+ "icon"
+ ],
+ "time": "2016-03-10 18:47:19"
+ },
+ {
+ "name": "imagine/imagine",
+ "version": "0.5.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/avalanche123/Imagine.git",
+ "reference": "343580fceed1f89220481ac98480e92f47d91e6c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/avalanche123/Imagine/zipball/343580fceed1f89220481ac98480e92f47d91e6c",
+ "reference": "343580fceed1f89220481ac98480e92f47d91e6c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "require-dev": {
+ "sami/sami": "dev-master"
+ },
+ "suggest": {
+ "ext-gd": "to use the GD implementation",
+ "ext-gmagick": "to use the Gmagick implementation",
+ "ext-imagick": "to use the Imagick implementation"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-develop": "0.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Imagine": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bulat Shakirzyanov",
+ "email": "mallluhuct@gmail.com",
+ "homepage": "http://avalanche123.com"
+ }
+ ],
+ "description": "Image processing for PHP 5.3",
+ "homepage": "http://imagine.readthedocs.org/",
+ "keywords": [
+ "drawing",
+ "graphics",
+ "image manipulation",
+ "image processing"
+ ],
+ "time": "2014-06-13 10:54:04"
+ },
+ {
+ "name": "kartik-v/yii2-krajee-base",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kartik-v/yii2-krajee-base.git",
+ "reference": "3e491e51ed742663b239cd6e0b7f76d403bed7e1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kartik-v/yii2-krajee-base/zipball/3e491e51ed742663b239cd6e0b7f76d403bed7e1",
+ "reference": "3e491e51ed742663b239cd6e0b7f76d403bed7e1",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2-bootstrap": "@dev"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.8.x-dev"
+ }
+ },
+ "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": "2016-03-03 12:24:13"
+ },
+ {
+ "name": "kartik-v/yii2-widget-colorinput",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kartik-v/yii2-widget-colorinput.git",
+ "reference": "18537fcdab0f5491d5eebff8e2464ef6a616ee4c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kartik-v/yii2-widget-colorinput/zipball/18537fcdab0f5491d5eebff8e2464ef6a616ee4c",
+ "reference": "18537fcdab0f5491d5eebff8e2464ef6a616ee4c",
+ "shasum": ""
+ },
+ "require": {
+ "kartik-v/yii2-krajee-base": "*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "kartik\\color\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD 3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Kartik Visweswaran",
+ "email": "kartikv2@gmail.com",
+ "homepage": "http://www.krajee.com/"
+ }
+ ],
+ "description": "An enhanced Yii 2 widget encapsulating the HTML 5 color input (sub repo split from yii2-widgets)",
+ "homepage": "https://github.com/kartik-v/yii2-widget-colorinput",
+ "keywords": [
+ "HTML5",
+ "color",
+ "extension",
+ "form",
+ "input",
+ "jquery",
+ "plugin",
+ "widget",
+ "yii2"
+ ],
+ "time": "2016-02-02 14:28:12"
+ },
+ {
+ "name": "kartik-v/yii2-widget-select2",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kartik-v/yii2-widget-select2.git",
+ "reference": "cb2a5992cb96bd2939e30ec1c76eba418d6a30af"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kartik-v/yii2-widget-select2/zipball/cb2a5992cb96bd2939e30ec1c76eba418d6a30af",
+ "reference": "cb2a5992cb96bd2939e30ec1c76eba418d6a30af",
+ "shasum": ""
+ },
+ "require": {
+ "kartik-v/yii2-krajee-base": "~1.7"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "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": "2016-03-10 11:33:59"
+ },
+ {
+ "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": "dev-master",
+ "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": "mihaildev/yii2-elfinder",
+ "version": "1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MihailDev/yii2-elfinder.git",
+ "reference": "64b42572dec94e5c2d0d1630bce8292848a98f4b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MihailDev/yii2-elfinder/zipball/64b42572dec94e5c2d0d1630bce8292848a98f4b",
+ "reference": "64b42572dec94e5c2d0d1630bce8292848a98f4b",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2-jui": "*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "asset-installer-paths": {
+ "npm-asset-library": "vendor/npm",
+ "bower-asset-library": "vendor/bower"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "mihaildev\\elfinder\\": ""
+ }
+ },
+ "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 ElFinder",
+ "homepage": "https://github.com/MihailDev/yii2-elfinder",
+ "keywords": [
+ "elfinder",
+ "filemanager",
+ "yii"
+ ],
+ "time": "2015-07-03 07:08:52"
+ },
+ {
+ "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": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Nodge/yii2-eauth.git",
+ "reference": "f45efd95e3853db33153cc1b856d1f648d221938"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Nodge/yii2-eauth/zipball/f45efd95e3853db33153cc1b856d1f648d221938",
+ "reference": "f45efd95e3853db33153cc1b856d1f648d221938",
+ "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": "2016-01-13 18:15:48"
+ },
+ {
+ "name": "rmrevin/yii2-comments",
+ "version": "1.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/rmrevin/yii2-comments.git",
+ "reference": "c68ddf276fe24ece0173781a8f7391a2cf7e6a12"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/rmrevin/yii2-comments/zipball/c68ddf276fe24ece0173781a8f7391a2cf7e6a12",
+ "reference": "c68ddf276fe24ece0173781a8f7391a2cf7e6a12",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "rmrevin/yii2-fontawesome": "~2.10",
+ "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\\module\\Comments\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Roman Revin",
+ "email": "xgismox@gmail.com",
+ "homepage": "http://rmrevin.ru/"
+ }
+ ],
+ "description": "Comments module for Yii2",
+ "keywords": [
+ "comment",
+ "module",
+ "widget",
+ "yii"
+ ],
+ "time": "2016-03-01 13:18:41"
+ },
+ {
+ "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": "5.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/swiftmailer/swiftmailer.git",
+ "reference": "fffbc0e2a7e376dbb0a4b5f2ff6847330f20ccf9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/fffbc0e2a7e376dbb0a4b5f2ff6847330f20ccf9",
+ "reference": "fffbc0e2a7e376dbb0a4b5f2ff6847330f20ccf9",
+ "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": "2016-01-03 15:42:47"
+ },
+ {
+ "name": "yiisoft/yii2",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-framework.git",
+ "reference": "88ceadba3fc19ec90f72b6ba895347b7fecd3dfb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/88ceadba3fc19ec90f72b6ba895347b7fecd3dfb",
+ "reference": "88ceadba3fc19ec90f72b6ba895347b7fecd3dfb",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/jquery": "2.2.*@stable | 2.1.*@stable | 1.11.*@stable",
+ "bower-asset/jquery.inputmask": "~3.2.2",
+ "bower-asset/punycode": "1.3.*",
+ "bower-asset/yii2-pjax": "~2.0.1",
+ "cebe/markdown": "~1.0.0 | ~1.1.0",
+ "ext-ctype": "*",
+ "ext-mbstring": "*",
+ "ezyang/htmlpurifier": "~4.6",
+ "lib-pcre": "*",
+ "php": ">=5.4.0",
+ "yiisoft/yii2-composer": "~2.0.4"
+ },
+ "bin": [
+ "yii"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Qiang Xue",
+ "email": "qiang.xue@gmail.com",
+ "homepage": "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"
+ },
+ {
+ "name": "Dmitry Naumenko",
+ "email": "d.naumenko.a@gmail.com",
+ "role": "Core framework development"
+ }
+ ],
+ "description": "Yii PHP Framework Version 2",
+ "homepage": "http://www.yiiframework.com/",
+ "keywords": [
+ "framework",
+ "yii2"
+ ],
+ "time": "2016-03-14 12:11:50"
+ },
+ {
+ "name": "yiisoft/yii2-bootstrap",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-bootstrap.git",
+ "reference": "4133d6b26f48615de38ea1ec04eb00c4b7057bc9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-bootstrap/zipball/4133d6b26f48615de38ea1ec04eb00c4b7057bc9",
+ "reference": "4133d6b26f48615de38ea1ec04eb00c4b7057bc9",
+ "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": "2016-03-04 00:48:53"
+ },
+ {
+ "name": "yiisoft/yii2-composer",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-composer.git",
+ "reference": "d33d1046a5951f2f7823fe343f28ddc58b3421a4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/d33d1046a5951f2f7823fe343f28ddc58b3421a4",
+ "reference": "d33d1046a5951f2f7823fe343f28ddc58b3421a4",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.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": "2016-02-06 01:03:32"
+ },
+ {
+ "name": "yiisoft/yii2-imagine",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-imagine.git",
+ "reference": "d87e6a0d1adfd6fa5ef49c18228b2fc9bca04299"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-imagine/zipball/d87e6a0d1adfd6fa5ef49c18228b2fc9bca04299",
+ "reference": "d87e6a0d1adfd6fa5ef49c18228b2fc9bca04299",
+ "shasum": ""
+ },
+ "require": {
+ "imagine/imagine": "0.5.*",
+ "yiisoft/yii2": "*"
+ },
+ "type": "yii2-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "yii\\imagine\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Antonio Ramirez",
+ "email": "amigo.cobos@gmail.com"
+ }
+ ],
+ "description": "The Imagine integration for the Yii framework",
+ "keywords": [
+ "helper",
+ "image",
+ "imagine",
+ "yii2"
+ ],
+ "time": "2016-02-21 23:09:41"
+ },
+ {
+ "name": "yiisoft/yii2-jui",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-jui.git",
+ "reference": "1425ab29929dd195f468d3c4eb340ab509f28b83"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-jui/zipball/1425ab29929dd195f468d3c4eb340ab509f28b83",
+ "reference": "1425ab29929dd195f468d3c4eb340ab509f28b83",
+ "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-12-24 06:23:53"
+ },
+ {
+ "name": "yiisoft/yii2-swiftmailer",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-swiftmailer.git",
+ "reference": "2cca1bb86444a6438b0720f8c60120a400982366"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-swiftmailer/zipball/2cca1bb86444a6438b0720f8c60120a400982366",
+ "reference": "2cca1bb86444a6438b0720f8c60120a400982366",
+ "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-12-07 11:40:31"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "bower-asset/typeahead.js",
+ "version": "v0.11.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/twitter/typeahead.js.git",
+ "reference": "588440f66559714280628a4f9799f0c4eb880a4a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/twitter/typeahead.js/zipball/588440f66559714280628a4f9799f0c4eb880a4a",
+ "reference": "588440f66559714280628a4f9799f0c4eb880a4a",
+ "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": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fzaninotto/Faker.git",
+ "reference": "588da2fff1b9da3acb9b6e1d7395e820102bde01"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/588da2fff1b9da3acb9b6e1d7395e820102bde01",
+ "reference": "588da2fff1b9da3acb9b6e1d7395e820102bde01",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "ext-intl": "*",
+ "phpunit/phpunit": "~4.0",
+ "squizlabs/php_codesniffer": "~1.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6.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": "2016-03-10 07:12:46"
+ },
+ {
+ "name": "phpspec/php-diff",
+ "version": "dev-master",
+ "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": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-codeception.git",
+ "reference": "ee239c244fd011f11c8827f97ebe7600d03f1841"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-codeception/zipball/ee239c244fd011f11c8827f97ebe7600d03f1841",
+ "reference": "ee239c244fd011f11c8827f97ebe7600d03f1841",
+ "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-11-20 08:52:21"
+ },
+ {
+ "name": "yiisoft/yii2-debug",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-debug.git",
+ "reference": "b258732b10a706d3fa41829f13be727e49dabd09"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-debug/zipball/b258732b10a706d3fa41829f13be727e49dabd09",
+ "reference": "b258732b10a706d3fa41829f13be727e49dabd09",
+ "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": "2016-03-14 21:47:38"
+ },
+ {
+ "name": "yiisoft/yii2-faker",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-faker.git",
+ "reference": "186c77214e0a4b75f10380b4e6e5f82788922cb7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-faker/zipball/186c77214e0a4b75f10380b4e6e5f82788922cb7",
+ "reference": "186c77214e0a4b75f10380b4e6e5f82788922cb7",
+ "shasum": ""
+ },
+ "require": {
+ "fzaninotto/faker": "~1.4",
+ "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-12-18 01:52:12"
+ },
+ {
+ "name": "yiisoft/yii2-gii",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yiisoft/yii2-gii.git",
+ "reference": "ce42838abcbef076ebaf46147671d518ae69d028"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yiisoft/yii2-gii/zipball/ce42838abcbef076ebaf46147671d518ae69d028",
+ "reference": "ce42838abcbef076ebaf46147671d518ae69d028",
+ "shasum": ""
+ },
+ "require": {
+ "bower-asset/typeahead.js": "0.10.* | ~0.11.0",
+ "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": "2016-02-21 20:39:29"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "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..6b649f4
--- /dev/null
+++ b/console/migrations/m130524_201442_init.php
@@ -0,0 +1,33 @@
+db->driverName === 'mysql') {
+ // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
+ $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
+ }
+
+ $this->createTable('{{%user}}', [
+ 'id' => $this->primaryKey(),
+ 'username' => $this->string()->notNull()->unique(),
+ 'auth_key' => $this->string(32)->notNull(),
+ 'password_hash' => $this->string()->notNull(),
+ 'password_reset_token' => $this->string()->unique(),
+ 'email' => $this->string()->notNull()->unique(),
+
+ 'status' => $this->smallInteger()->notNull()->defaultValue(10),
+ 'created_at' => $this->integer()->notNull(),
+ 'updated_at' => $this->integer()->notNull(),
+ ], $tableOptions);
+ }
+
+ public function down()
+ {
+ $this->dropTable('{{%user}}');
+ }
+}
diff --git a/console/migrations/m160126_071717_rubrication.php b/console/migrations/m160126_071717_rubrication.php
new file mode 100644
index 0000000..200cb49
--- /dev/null
+++ b/console/migrations/m160126_071717_rubrication.php
@@ -0,0 +1,135 @@
+db->driverName === 'mysql') {
+ // Only for MySQL
+ $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
+
+ // @todo https://habrahabr.ru/post/138947/
+ } elseif ($this->db->driverName === 'pgsql') {
+ // Only for PostgreSQL
+ // @todo use intarray field for tax_options
+ }
+ $this->createTable('{{%tax_group}}', [
+ 'tax_group_id' => $this->primaryKey(),
+ 'alias' => $this->string(50)->notNull(),
+ 'name' => $this->string(255)->notNull(),
+ 'description' => $this->text(),
+ 'module' => $this->string(50)->notNull(),
+ 'hierarchical' => $this->boolean()->notNull()->defaultValue(false),
+ 'settings' => $this->text()
+ ], $tableOptions);
+
+ $this->createTable('{{%tax_option}}', [
+ 'tax_option_id' => $this->bigPrimaryKey(),
+ 'tax_group_id' => $this->integer()->notNull(),
+ 'parent_id' => $this->integer()->notNull()->defaultValue(0),
+ 'path' => $this->string(),
+ 'depth' => $this->integer(),
+ 'sort' => $this->integer()->notNull()->defaultValue(0),
+ 'default_value' => $this->integer(),
+ 'alias' => $this->string(50)->notNull()->defaultValue(''),
+ ], $tableOptions);
+ $this->addForeignKey('fki_tax_option_tax_group_id', 'tax_option', 'tax_group_id', 'tax_group', 'tax_group_id', 'CASCADE', 'CASCADE');
+
+ $this->createTable('{{%tax_group_to_group}}', [
+ 'tax_group1_id' => $this->integer()->notNull(),
+ 'tax_group2_id' => $this->integer()->notNull(),
+ 'alias' => $this->string(50)->notNull(),
+ 'sort' => $this->integer(),
+ ], $tableOptions);
+ $this->addForeignKey('fki_tax_group_to_group1', 'tax_group_to_group', 'tax_group1_id', 'tax_group', 'tax_group_id', 'CASCADE', 'CASCADE');
+ $this->addForeignKey('fki_tax_group_to_group2', 'tax_group_to_group', 'tax_group2_id', 'tax_group', 'tax_group_id', 'CASCADE', 'CASCADE');
+ $this->addPrimaryKey('pki_tax_group_to_group', 'tax_group_to_group', ['tax_group1_id', 'tax_group2_id', 'alias']);
+
+ $this->createTable('{{%tax_option_to_group}}', [
+ 'tax_option_id' => $this->integer()->notNull(),
+ 'tax_group_id' => $this->integer()->notNull(),
+ 'alias' => $this->string(50)->notNull(),
+ 'sort' => $this->integer(),
+ ], $tableOptions);
+ $this->addForeignKey('tax_option_to_group_option_id', 'tax_option_to_group', 'tax_option_id', 'tax_option', 'tax_option_id', 'CASCADE', 'CASCADE');
+ $this->addForeignKey('tax_option_to_group_group_id', 'tax_option_to_group', 'tax_group_id', 'tax_group', 'tax_group_id', 'CASCADE', 'CASCADE');
+ $this->addPrimaryKey('pki_tax_option_to_group', 'tax_option_to_group', ['tax_option_id', 'tax_group_id', 'alias']);
+
+ $this->createTable('{{%tax_option_to_option}}', [
+ 'tax_option1_id' => $this->integer()->notNull(),
+ 'tax_option2_id' => $this->integer()->notNull(),
+ 'alias' => $this->string(50)->notNull(),
+ 'sort' => $this->integer(),
+ ], $tableOptions);
+ $this->addForeignKey('fki_tax_option_to_option1', 'tax_option_to_option', 'tax_option1_id', 'tax_option', 'tax_option_id', 'CASCADE', 'CASCADE');
+ $this->addForeignKey('fki_tax_option_to_option2', 'tax_option_to_option', 'tax_option2_id', 'tax_option', 'tax_option_id', 'CASCADE', 'CASCADE');
+ $this->addPrimaryKey('pki_tax_option_to_option', 'tax_option_to_option', ['tax_option1_id', 'tax_option2_id', 'alias']);
+
+ /*$this->createTable('{{%tax_entity_relation}}', [
+ 'tax_option_id' => $this->integer()->notNull(),
+ 'entity_id' => $this->integer()->notNull(),
+ 'entity_table_name' => $this->string(50)->notNull(),
+ 'entity_key_name' => $this->string(50)->notNull(),
+ 'alias' => $this->string(50)->notNull(),
+ ], $tableOptions);
+ $this->addForeignKey('fki_tax_entity_relation_option_id', 'tax_entity_relation', 'tax_option_id', 'tax_option', 'tax_option_id', 'CASCADE', 'CASCADE');
+ $this->addPrimaryKey('pki_tax_entity_relation', 'tax_entity_relation', ['tax_option_id', 'entity_id', 'entity_table_name', 'entity_key_name', 'alias']);*/
+
+ $this->createTable('{{%tax_value_int}}', [
+ 'tax_value_id' => $this->primaryKey(),
+ 'tax_option_id' => $this->integer()->notNull(),
+ 'value' => $this->integer(),
+ ], $tableOptions);
+ $this->addForeignKey('fki_tax_value_int_option_id', 'tax_value_int', 'tax_option_id', 'tax_option', 'tax_option_id', 'CASCADE', 'CASCADE');
+
+ $this->createTable('{{%tax_value_string}}', [
+ 'tax_value_id' => $this->primaryKey(),
+ 'tax_option_id' => $this->integer()->notNull(),
+ 'value' => $this->string(),
+ ], $tableOptions);
+ $this->addForeignKey('fki_tax_value_string_option_id', 'tax_value_string', 'tax_option_id', 'tax_option', 'tax_option_id', 'CASCADE', 'CASCADE');
+
+ $this->createTable('{{%tax_value_text}}', [
+ 'tax_value_id' => $this->primaryKey(),
+ 'tax_option_id' => $this->integer()->notNull(),
+ 'value' => $this->text(),
+ ], $tableOptions);
+ $this->addForeignKey('fki_tax_value_text_option_id', 'tax_value_text', 'tax_option_id', 'tax_option', 'tax_option_id', 'CASCADE', 'CASCADE');
+
+ $this->createTable('{{%tax_value_float}}', [
+ 'tax_value_id' => $this->bigPrimaryKey(),
+ 'tax_option_id' => $this->integer()->notNull(),
+ 'value' => $this->float(),
+ ], $tableOptions);
+ $this->addForeignKey('fki_tax_value_float_option_id', 'tax_value_float', 'tax_option_id', 'tax_option', 'tax_option_id', 'CASCADE', 'CASCADE');
+ }
+
+ public function safeDown()
+ {
+ $this->dropTable('{{%tax_value_int}}');
+ $this->dropTable('{{%tax_value_string}}');
+ $this->dropTable('{{%tax_value_text}}');
+ $this->dropTable('{{%tax_value_float}}');
+// $this->dropTable('{{%tax_entity_relation}}');
+ $this->dropTable('{{%tax_group_to_group}}');
+ $this->dropTable('{{%tax_option_to_group}}');
+ $this->dropTable('{{%tax_option_to_option}}');
+ $this->dropTable('{{%tax_option}}');
+ $this->dropTable('{{%tax_group}}');
+ }
+
+ /*
+ // Use safeUp/safeDown to run migration code within a transaction
+ public function safeUp()
+ {
+ }
+
+ public function safeDown()
+ {
+ }
+ */
+}
diff --git a/console/migrations/m160128_101543_fields.php b/console/migrations/m160128_101543_fields.php
new file mode 100644
index 0000000..cb49e9e
--- /dev/null
+++ b/console/migrations/m160128_101543_fields.php
@@ -0,0 +1,29 @@
+createTable('{{%fields}}', [
+ 'id' => $this->primaryKey(),
+ 'table_name' => $this->string(255)->notNull(),
+ 'table_id' => $this->integer(),
+ 'value' => $this->string(255),
+ 'field_name' => $this->string(),
+ 'field_type' => $this->string(32)->notNull(),
+ 'language' => $this->string(3),
+ 'key' => $this->integer(),
+ 'parent_key' => $this->integer()
+ ], $tableOptions);
+
+ }
+
+ public function down()
+ {
+ $this->dropTable('{{%fields}}');
+ }
+}
diff --git a/console/migrations/m160208_111900_blog.php b/console/migrations/m160208_111900_blog.php
new file mode 100644
index 0000000..ca88b1c
--- /dev/null
+++ b/console/migrations/m160208_111900_blog.php
@@ -0,0 +1,30 @@
+createTable('{{%blog}}', [
+ 'blog_id' => $this->primaryKey(),
+ 'user_id' => $this->integer()->notNull(),
+ 'name' => $this->string(255)->notNull(),
+ 'link' => $this->string(255),
+ 'date_add' => $this->timestamp()->notNull(),
+ 'user_add_id' => $this->integer(),
+ 'view_count' => $this->integer()->defaultValue(0),
+ 'description' => $this->text(),
+ 'cover' => $this->string(255),
+ ], $tableOptions);
+
+ }
+
+ public function down()
+ {
+ $this->dropTable('{{%blog}}');
+ }
+
+}
diff --git a/console/migrations/m160304_054017_realtion.php b/console/migrations/m160304_054017_realtion.php
new file mode 100644
index 0000000..4f5b11c
--- /dev/null
+++ b/console/migrations/m160304_054017_realtion.php
@@ -0,0 +1,44 @@
+db->driverName === 'mysql') {
+ // Only for MySQL
+ $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
+
+ // @todo https://habrahabr.ru/post/138947/
+ } elseif ($this->db->driverName === 'pgsql') {
+ // Only for PostgreSQL
+ // @todo use intarray field for tax_options
+ }
+ $this->createTable('{{%relation}}', [
+ 'alias' => $this->string(50)->notNull(),
+ 'entity1_id' => $this->integer()->notNull(),
+ 'entity2_id' => $this->integer()->notNull(),
+ ], $tableOptions);
+ $this->addPrimaryKey('relation_ukey', 'relation', ['alias', 'entity1_id', 'entity2_id']);
+ }
+
+ public function down()
+ {
+ $this->dropTable('{{%relation}}');
+
+ return false;
+ }
+
+ /*
+ // Use safeUp/safeDown to run migration code within a transaction
+ public function safeUp()
+ {
+ }
+
+ public function safeDown()
+ {
+ }
+ */
+}
diff --git a/console/migrations/m160304_065108_product.php b/console/migrations/m160304_065108_product.php
new file mode 100644
index 0000000..7925483
--- /dev/null
+++ b/console/migrations/m160304_065108_product.php
@@ -0,0 +1,50 @@
+db->driverName === 'mysql') {
+ // Only for MySQL
+ $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
+
+ // @todo https://habrahabr.ru/post/138947/
+ } elseif ($this->db->driverName === 'pgsql') {
+ // Only for PostgreSQL
+ // @todo use intarray field for tax_options
+ }
+ $this->createTable('{{%product}}', [
+ 'product_id' => $this->primaryKey(),
+ 'name' => $this->string(255)->notNull(),
+ ], $tableOptions);
+
+ $this->createTable('{{%product_category}}', [
+ 'product_id' => $this->integer()->notNull(),
+ 'category_id' => $this->integer()->notNull(),
+ ], $tableOptions);
+ $this->addForeignKey('fki_product_id', 'product_category', 'product_id', 'product', 'product_id', 'NO ACTION', 'NO ACTION');
+ $this->addForeignKey('fki_category_id', 'product_category', 'category_id', 'tax_option', 'tax_option_id', 'NO ACTION', 'NO ACTION');
+ }
+
+ public function down()
+ {
+ $this->dropTable('{{%product}}');
+ $this->dropTable('{{%product_category}}');
+
+ return false;
+ }
+
+ /*
+ // Use safeUp/safeDown to run migration code within a transaction
+ public function safeUp()
+ {
+ }
+
+ public function safeDown()
+ {
+ }
+ */
+}
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/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'] = [
+ 'class' => 'yii\debug\Module',
+ ];
+ $config['bootstrap'][] = 'gii';
+ $config['modules']['gii'] = [
+ 'class' => '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/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'],
+ 'controllerNamespace' => 'frontend\controllers',
+ 'components' => [
+ 'user' => [
+ 'identityClass' => 'common\models\User',
+ 'enableAutoLogin' => true,
+ 'identityCookie' => [
+ 'name' => '_frontendUser', // unique for frontend
+ ]
+ ],
+ 'request'=>[
+ 'cookieValidationKey' => 'ndahjhjjidasuidrqeswuiuirqw89',
+ 'csrfParam' => '_frontendCSRF',
+ 'class' => 'common\components\Request',
+
+ 'web'=> '/frontend/web'
+
+ ],
+
+ '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' => [
+ ]
+ ]
+
+ ],
+ 'params' => $params,
+];
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/SiteController.php b/frontend/controllers/SiteController.php
new file mode 100644
index 0000000..cf691ae
--- /dev/null
+++ b/frontend/controllers/SiteController.php
@@ -0,0 +1,213 @@
+ [
+ 'class' => AccessControl::className(),
+ 'only' => ['logout', 'signup'],
+ 'rules' => [
+ [
+ 'actions' => ['signup'],
+ 'allow' => true,
+ 'roles' => ['?'],
+ ],
+ [
+ 'actions' => ['logout'],
+ 'allow' => true,
+ 'roles' => ['@'],
+ ],
+ ],
+ ],
+ 'verbs' => [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'logout' => ['post'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function actions()
+ {
+ return [
+ 'error' => [
+ 'class' => 'yii\web\ErrorAction',
+ ],
+ 'captcha' => [
+ 'class' => 'yii\captcha\CaptchaAction',
+ 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
+ ],
+ ];
+ }
+
+ /**
+ * Displays homepage.
+ *
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ return $this->render('index');
+ }
+
+ /**
+ * Logs in a user.
+ *
+ * @return mixed
+ */
+ public function actionLogin()
+ {
+ if (!\Yii::$app->user->isGuest) {
+ return $this->goHome();
+ }
+
+ $model = new LoginForm();
+ if ($model->load(Yii::$app->request->post()) && $model->login()) {
+ return $this->goBack();
+ } else {
+ return $this->render('login', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Logs out the current user.
+ *
+ * @return mixed
+ */
+ public function actionLogout()
+ {
+ Yii::$app->user->logout();
+
+ return $this->goHome();
+ }
+
+ /**
+ * Displays contact page.
+ *
+ * @return mixed
+ */
+ public function actionContact()
+ {
+ $model = new ContactForm();
+ if ($model->load(Yii::$app->request->post()) && $model->validate()) {
+ if ($model->sendEmail(Yii::$app->params['adminEmail'])) {
+ Yii::$app->session->setFlash('success', 'Thank you for contacting us. We will respond to you as soon as possible.');
+ } else {
+ Yii::$app->session->setFlash('error', 'There was an error sending email.');
+ }
+
+ return $this->refresh();
+ } else {
+ return $this->render('contact', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Displays about page.
+ *
+ * @return mixed
+ */
+ public function actionAbout()
+ {
+ return $this->render('about');
+ }
+
+ /**
+ * Signs user up.
+ *
+ * @return mixed
+ */
+ public function actionSignup()
+ {
+ $model = new SignupForm();
+ if ($model->load(Yii::$app->request->post())) {
+ 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,
+ ]);
+ }
+}
diff --git a/frontend/models/ContactForm.php b/frontend/models/ContactForm.php
new file mode 100644
index 0000000..613abb5
--- /dev/null
+++ b/frontend/models/ContactForm.php
@@ -0,0 +1,59 @@
+ '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/PasswordResetRequestForm.php b/frontend/models/PasswordResetRequestForm.php
new file mode 100644
index 0000000..c09f6f7
--- /dev/null
+++ b/frontend/models/PasswordResetRequestForm.php
@@ -0,0 +1,68 @@
+ '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) {
+ return false;
+ }
+
+ if (!User::isPasswordResetTokenValid($user->password_reset_token)) {
+ $user->generatePasswordResetToken();
+ }
+
+ if (!$user->save()) {
+ return false;
+ }
+
+ return Yii::$app
+ ->mailer
+ ->compose(
+ ['html' => 'passwordResetToken-html', 'text' => 'passwordResetToken-text'],
+ ['user' => $user]
+ )
+ ->setFrom([\Yii::$app->params['supportEmail'] => \Yii::$app->name . ' robot'])
+ ->setTo($this->email)
+ ->setSubject('Password reset for ' . \Yii::$app->name)
+ ->send();
+ }
+}
diff --git a/frontend/models/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..bd6722f
--- /dev/null
+++ b/frontend/models/SignupForm.php
@@ -0,0 +1,58 @@
+ '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()) {
+ return null;
+ }
+
+ $user = new User();
+ $user->username = $this->username;
+ $user->email = $this->email;
+ $user->setPassword($this->password);
+ $user->generateAuthKey();
+
+ return $user->save() ? $user : 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/layouts/main.php b/frontend/views/layouts/main.php
new file mode 100644
index 0000000..c86aa9b
--- /dev/null
+++ b/frontend/views/layouts/main.php
@@ -0,0 +1,82 @@
+
+beginPage() ?>
+
+
+
+
+
+ = Html::csrfMetaTags() ?>
+ = Html::encode($this->title) ?>
+ head() ?>
+
+
+beginBody() ?>
+
+
+ 'My Company',
+ 'brandUrl' => Yii::$app->homeUrl,
+ 'options' => [
+ 'class' => 'navbar-inverse navbar-fixed-top',
+ ],
+ ]);
+ $menuItems = [
+ ['label' => 'Home', 'url' => ['/site/index']],
+ ['label' => 'About', 'url' => ['/site/about']],
+ ['label' => 'Contact', 'url' => ['/site/contact']],
+ ];
+ if (Yii::$app->user->isGuest) {
+ $menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']];
+ $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];
+ } else {
+ $menuItems[] = '
'
+ . Html::beginForm(['/site/logout'], 'post')
+ . Html::submitButton(
+ 'Logout (' . Yii::$app->user->identity->username . ')',
+ ['class' => 'btn btn-link']
+ )
+ . Html::endForm()
+ . ' ';
+ }
+ 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/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..dcad18f
--- /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/index.php b/frontend/views/site/index.php
new file mode 100644
index 0000000..f780610
--- /dev/null
+++ b/frontend/views/site/index.php
@@ -0,0 +1,53 @@
+title = 'My Yii Application';
+?>
+
+
+
+
Congratulations!
+
+
You have successfully created your Yii-powered application.
+
+
Get started with Yii
+
+
+
+
+
+
+
Heading
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
+ dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
+ ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
+ fugiat nulla pariatur.
+
+
Yii Documentation »
+
+
+
Heading
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
+ dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
+ ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
+ fugiat nulla pariatur.
+
+
Yii Forum »
+
+
+
Heading
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
+ dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
+ ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
+ fugiat nulla pariatur.
+
+
Yii Extensions »
+
+
+
+
+
diff --git a/frontend/views/site/login.php b/frontend/views/site/login.php
new file mode 100644
index 0000000..56ea98e
--- /dev/null
+++ b/frontend/views/site/login.php
@@ -0,0 +1,39 @@
+title = 'Login';
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
= Html::encode($this->title) ?>
+
+
Please fill out the following fields to login:
+
+
+
+ 'login-form']); ?>
+
+ = $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
+
+ = $form->field($model, 'password')->passwordInput() ?>
+
+ = $form->field($model, 'rememberMe')->checkbox() ?>
+
+
+ If you forgot your password you can = Html::a('reset it', ['site/request-password-reset']) ?>.
+
+
+
+ = Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
+
+
+
+
+
+
diff --git a/frontend/views/site/requestPasswordResetToken.php b/frontend/views/site/requestPasswordResetToken.php
new file mode 100644
index 0000000..9f6822e
--- /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')->textInput(['autofocus' => true]) ?>
+
+
+ = 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..36ef452
--- /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(['autofocus' => true]) ?>
+
+
+ = 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..de9dad6
--- /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')->textInput(['autofocus' => true]) ?>
+
+ = $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/site.css b/frontend/web/css/site.css
new file mode 100644
index 0000000..25df5fb
--- /dev/null
+++ b/frontend/web/css/site.css
@@ -0,0 +1,105 @@
+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: "\e151";
+}
+
+a.desc:after {
+ content: "\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 td {
+ white-space: nowrap;
+}
+
+.grid-view .filters input,
+.grid-view .filters select {
+ min-width: 50px;
+}
+
+.hint-block {
+ display: block;
+ margin-top: 5px;
+ color: #999;
+}
+
+.error-summary {
+ color: #a94442;
+ background: #fdf7f7;
+ border-left: 3px solid #eed3d7;
+ padding: 10px 20px;
+ margin: 0 0 15px 0;
+}
+
+/* align the logout "link" (button in form) of the navbar */
+.nav > li > form {
+ padding: 8px;
+}
+
+.nav > li > form > button:hover {
+ text-decoration: none;
+}
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/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..6b8dd76
--- /dev/null
+++ b/init
@@ -0,0 +1,213 @@
+#!/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) {
+ if (is_dir("$root/$writable")) {
+ echo " chmod 0777 $writable\n";
+ @chmod("$root/$writable", 0777);
+ } else {
+ echo "\n Error. Directory $writable does not exist. \n";
+ }
+ }
+}
+
+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/requirements.php b/requirements.php
new file mode 100644
index 0000000..fd84f47
--- /dev/null
+++ b/requirements.php
@@ -0,0 +1,132 @@
+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/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->see('Logout (erau)', 'form button[type=submit]');
+$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->see('Logout (erau)', 'form button[type=submit]');
+$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..63739ef
--- /dev/null
+++ b/tests/codeception/common/_support/FixtureHelper.php
@@ -0,0 +1,73 @@
+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..5e989a5
--- /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 address 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..7c433af
--- /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->see('Logout (erau)', 'form button[type=submit]');
+$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..e8431e5
--- /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->see('Logout (tester)', 'form button[type=submit]');
+ }
+}
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..a93d7e4
--- /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 address 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..e1d2f9b
--- /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->see('Logout (erau)', 'form button[type=submit]');
+$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..16410de
--- /dev/null
+++ b/tests/codeception/frontend/functional/SignupCest.php
@@ -0,0 +1,88 @@
+loadFixtures();
+ }
+
+ /**
+ * This method is called when test fails.
+ * @param \codeception_frontend\FunctionalTester $I
+ */
+ public function _failed($I)
+ {
+
+ }
+
+ /**
+ *
+ * @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->see('Logout (tester)', 'form button[type=submit]');
+ }
+}
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