diff --git a/backend/assets/AppAsset.php b/backend/assets/AppAsset.php
index 940c0af..b290729 100644
--- a/backend/assets/AppAsset.php
+++ b/backend/assets/AppAsset.php
@@ -16,6 +16,9 @@ class AppAsset extends AssetBundle
];
public $js = [
];
+ public $jsOptions = [
+ 'position' => \yii\web\View::POS_HEAD
+ ];
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
diff --git a/backend/controllers/SiteController.php b/backend/controllers/SiteController.php
index 01d9ad8..e1bc8e3 100644
--- a/backend/controllers/SiteController.php
+++ b/backend/controllers/SiteController.php
@@ -6,6 +6,7 @@
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
use common\models\LoginForm;
+use common\models\User;
/**
* Site controller
@@ -29,6 +30,10 @@ public function behaviors()
'actions' => ['logout', 'index'],
'allow' => true,
'roles' => ['@'],
+ 'matchCallback' => function ($rule, $action)
+ {
+ return Yii::$app->user->identity['role'] == User::ROLE_ADMIN;
+ }
],
],
],
@@ -65,7 +70,7 @@ public function actionLogin()
}
$model = new LoginForm();
- if ($model->load(Yii::$app->request->post()) && $model->login()) {
+ if ($model->load(Yii::$app->request->post()) && $model->backendLogin()) {
return $this->goBack();
} else {
return $this->render('login', [
diff --git a/backend/controllers/UserController.php b/backend/controllers/UserController.php
new file mode 100644
index 0000000..0bc9512
--- /dev/null
+++ b/backend/controllers/UserController.php
@@ -0,0 +1,236 @@
+ [
+ 'class' => AccessControl::className(),
+ 'rules' => [
+ [
+ 'actions' => ['index', 'view', 'edit', 'create', 'update', 'delete', 'multipledelete', 'profile'],
+ 'allow' => true,
+ 'roles' => ['@'],
+ 'matchCallback' => function ($rule, $action)
+ {
+ return
+ Yii::$app->user->identity['role'] == User::ROLE_SUPERADMIN ||
+ Yii::$app->user->identity['role'] == User::ROLE_ADMIN;
+ }
+ ],
+ ],
+ ],
+ 'verbs' => [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all User models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new UserSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single User model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new User model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new User();
+
+ if ($model->load(Yii::$app->request->post()))
+ {
+ if (strlen($model->password) < 6)
+ {
+ $model->addError('password', 'Password should contain at least 6 characters.');
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+
+ $model->setPassword($model->password);
+ $model->generateAuthKey();
+
+ if ($model->save())
+ return $this->redirect(['view', 'id' => $model->id]);
+ else
+ {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+
+ }
+ else
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+
+ /**
+ * Updates an existing User model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if (Yii::$app->request->post())
+ {
+ $post_user = Yii::$app->request->post('User');
+
+ $model->username = $post_user['username'];
+ $model->email = $post_user['email'];
+ $model->role = $post_user['role'];
+ $model->status = $post_user['status'];
+
+ // update password
+ if ( ! empty($post_user['password']))
+ {
+ if (strlen($model->password) < 6)
+ {
+ $model->addError('password', 'Password should contain at least 6 characters.');
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+
+ $model->setPassword($post_user['password']);
+ }
+
+ if ($model->save())
+ return $this->redirect(['view', 'id' => $model->id]);
+ else
+ return $this->render('update', ['model' => $model,]);
+ }
+ else
+ return $this->render('update', ['model' => $model]);
+ }
+
+ /**
+ * Updates the logged in user's profile
+ * @param boolean $success
+ * @return mixed
+ */
+ public function actionProfile($success = false)
+ {
+ $model = $this->findModel(Yii::$app->user->id);
+
+ if (Yii::$app->request->post())
+ {
+ $post_user = Yii::$app->request->post('User');
+
+ $model->username = $post_user['username'];
+
+ // update password
+ if ( ! empty($post_user['password']))
+ {
+ if (strlen($model->password) < 6)
+ {
+ $model->addError('password', 'Password should contain at least 6 characters.');
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+
+ $model->setPassword($post_user['password']);
+ }
+
+ if ($model->save())
+ return $this->redirect(['profile', 'success' => true]);
+ else
+ return $this->render('profile', ['model' => $model]);
+ }
+ else
+ return $this->render('profile', ['model' => $model, 'success' => $success]);
+ }
+
+ /**
+ * Deletes an existing User model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Delete multiplede IDs
+ * @return mixed
+ */
+ public function actionMultipledelete()
+ {
+ if (Yii::$app->request->isAjax)
+ {
+ $selected_ids = Yii::$app->request->post('selectedItems');
+ foreach ($selected_ids as $id)
+ $this->findModel($id)->delete();
+ }
+ }
+
+ /**
+ * Finds the User model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return User the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = User::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/backend/views/layouts/js/_multipledelete_script.php b/backend/views/layouts/js/_multipledelete_script.php
new file mode 100644
index 0000000..521fc41
--- /dev/null
+++ b/backend/views/layouts/js/_multipledelete_script.php
@@ -0,0 +1,32 @@
+
\ No newline at end of file
diff --git a/backend/views/layouts/main.php b/backend/views/layouts/main.php
index 62d5992..4da32e5 100644
--- a/backend/views/layouts/main.php
+++ b/backend/views/layouts/main.php
@@ -3,6 +3,7 @@
/* @var $this \yii\web\View */
/* @var $content string */
+use common\models\User;
use backend\assets\AppAsset;
use yii\helpers\Html;
use yii\bootstrap\Nav;
@@ -37,9 +38,22 @@
$menuItems = [
['label' => 'Home', 'url' => ['/site/index']],
];
- if (Yii::$app->user->isGuest) {
+ if (Yii::$app->user->isGuest)
+ {
$menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];
- } else {
+ }
+ elseif (Yii::$app->user->identity['role'] == User::ROLE_SUPERADMIN)
+ {
+ $menuItems[] = [
+ 'label' => 'Profile',
+ 'url' => ['/user/profile'],
+ ];
+
+ $menuItems[] = [
+ 'label' => 'User',
+ 'url' => ['/user/index'],
+ ];
+
$menuItems[] = '
'
. Html::beginForm(['/site/logout'], 'post')
. Html::submitButton(
diff --git a/backend/views/user/_form.php b/backend/views/user/_form.php
new file mode 100644
index 0000000..2d3d96b
--- /dev/null
+++ b/backend/views/user/_form.php
@@ -0,0 +1,32 @@
+
+
+
diff --git a/backend/views/user/_search.php b/backend/views/user/_search.php
new file mode 100644
index 0000000..781acce
--- /dev/null
+++ b/backend/views/user/_search.php
@@ -0,0 +1,45 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'id') ?>
+
+ = $form->field($model, 'username') ?>
+
+ = $form->field($model, 'auth_key') ?>
+
+ = $form->field($model, 'password_hash') ?>
+
+ = $form->field($model, 'password_reset_token') ?>
+
+ field($model, 'email') ?>
+
+ field($model, 'role') ?>
+
+ field($model, 'status') ?>
+
+ field($model, 'created_at') ?>
+
+ field($model, 'updated_at') ?>
+
+
+ = Html::submitButton(Yii::t('app', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('app', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/backend/views/user/create.php b/backend/views/user/create.php
new file mode 100644
index 0000000..52adf3c
--- /dev/null
+++ b/backend/views/user/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('app', 'Create User');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Users'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/backend/views/user/index.php b/backend/views/user/index.php
new file mode 100644
index 0000000..0bab8a1
--- /dev/null
+++ b/backend/views/user/index.php
@@ -0,0 +1,50 @@
+title = Yii::t('app', 'Users');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('app', 'Create User'), ['create'], ['class' => 'btn btn-success btn-sm']) ?>
+
+ Delete Selected
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\CheckboxColumn'],
+ [
+ 'attribute' => 'id',
+ 'options' => ['width' => '70px']
+ ],
+ 'username',
+ // 'auth_key',
+ // 'password_hash',
+ // 'password_reset_token',
+ 'email:email',
+ [
+ 'attribute' => 'role',
+ 'filter' => \common\models\User::getRoleAsArray()
+ ],
+ // 'status',
+ // 'created_at',
+ // 'updated_at',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+
+= $this->render('//layouts/js/_multipledelete_script') ?>
diff --git a/backend/views/user/profile.php b/backend/views/user/profile.php
new file mode 100644
index 0000000..960ee3d
--- /dev/null
+++ b/backend/views/user/profile.php
@@ -0,0 +1,43 @@
+title = Yii::t('app', 'Update Profile');
+?>
+
+
+
= Html::encode($this->title) ?>
+ = $success ?
+ yii\bootstrap\Alert::widget([
+ 'options' => [
+ 'class' => 'alert-info',
+ ],
+ 'body' => 'Profile updated.',
+ ]) : ''?>
+
+
+
+
+
diff --git a/backend/views/user/update.php b/backend/views/user/update.php
new file mode 100644
index 0000000..801d8b3
--- /dev/null
+++ b/backend/views/user/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('app', 'Update {modelClass}: ', [
+ 'modelClass' => 'User',
+]) . $model->username;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Users'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->username, 'url' => ['view', 'id' => $model->id]];
+$this->params['breadcrumbs'][] = Yii::t('app', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/backend/views/user/view.php b/backend/views/user/view.php
new file mode 100644
index 0000000..f9a06cf
--- /dev/null
+++ b/backend/views/user/view.php
@@ -0,0 +1,50 @@
+title = $model->username;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Users'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('app', 'Update'), ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('app', 'Delete'), ['delete', 'id' => $model->id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'id',
+ 'username',
+ // 'auth_key',
+ // 'password_hash',
+ // 'password_reset_token',
+ 'email:email',
+ 'role',
+ 'status',
+ [
+ 'attribute' => 'created_at',
+ 'value' => date('Y-m-d H:i:s', $model->created_at)
+ ],
+ [
+ 'attribute' => 'updated_at',
+ 'value' => date('Y-m-d H:i:s', $model->updated_at)
+ ],
+ ],
+ ]) ?>
+
+
diff --git a/common/models/LoginForm.php b/common/models/LoginForm.php
index ea0e968..bba712f 100644
--- a/common/models/LoginForm.php
+++ b/common/models/LoginForm.php
@@ -62,6 +62,28 @@ public function login()
}
}
+ /**
+ * Logs in a backend user using the provided email and password.
+ *
+ * @return boolean whether the user is logged in successfully
+ */
+ public function backendLogin()
+ {
+ if ($this->validate()) {
+ $user = $this->getUser();
+ if ($user->role == User::ROLE_USER)
+ {
+ $this->_user = false;
+ $this->addError('email', 'Can not login as ' . User::ROLE_USER);
+ return false;
+ }
+
+ return Yii::$app->user->login($user, $this->rememberMe ? 3600 * 24 * 30 : 0);
+ } else {
+ return false;
+ }
+ }
+
/**
* Finds user by [[email]]
*
diff --git a/common/models/User.php b/common/models/User.php
index d95ee47..eef5006 100644
--- a/common/models/User.php
+++ b/common/models/User.php
@@ -27,7 +27,10 @@ class User extends ActiveRecord implements IdentityInterface
const STATUS_ACTIVE = 10;
const ROLE_SUPERADMIN = 'superadmin';
- const ROLE_ADMIN = 'admin';
+ const ROLE_ADMIN = 'admin';
+ const ROLE_USER = 'user';
+
+ public $password;
/**
* @inheritdoc
@@ -53,8 +56,38 @@ public function behaviors()
public function rules()
{
return [
+ [['username', 'email', 'auth_key', 'password_hash'], 'required'],
+ ['username', 'string', 'min' => 4],
+ ['email', 'email'],
+ [['status', 'created_at', 'updated_at'], 'integer'],
['status', 'default', 'value' => self::STATUS_ACTIVE],
['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
+ [['username', 'password_hash', 'password_reset_token', 'email'], 'string', 'max' => 255],
+ [['auth_key'], 'string', 'max' => 32],
+ [['role'], 'string', 'max' => 50],
+ [['role'], 'default', 'value' => self::ROLE_USER],
+ [['email'], 'unique'],
+ [['password_reset_token'], 'unique'],
+ ['password', 'string', 'min' => 6]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'username' => Yii::t('app', 'User Name'),
+ 'auth_key' => Yii::t('app', 'Auth Key'),
+ 'password_hash' => Yii::t('app', 'Password Hash'),
+ 'password_reset_token' => Yii::t('app', 'Password Reset Token'),
+ 'email' => Yii::t('app', 'Email'),
+ 'role' => Yii::t('app', 'Role'),
+ 'status' => Yii::t('app', 'Status'),
+ 'created_at' => Yii::t('app', 'Created At'),
+ 'updated_at' => Yii::t('app', 'Updated At'),
];
}
@@ -199,4 +232,26 @@ public function removePasswordResetToken()
{
$this->password_reset_token = null;
}
+
+ /**
+ * Get `role` field as array
+ * @return array array of roles
+ */
+ public static function getRoleAsArray()
+ {
+ return [
+ static::ROLE_SUPERADMIN => 'Super Admin',
+ static::ROLE_ADMIN => 'Admin',
+ static::ROLE_USER => 'User',
+ ];
+ }
+
+ /**
+ * Get `status` field as array
+ * @return array array of status
+ */
+ public static function getStatusAsArray()
+ {
+ return [static::STATUS_ACTIVE => 'Active', static::STATUS_DELETED => 'Deleted'];
+ }
}
diff --git a/common/models/UserSearch.php b/common/models/UserSearch.php
new file mode 100644
index 0000000..41c15db
--- /dev/null
+++ b/common/models/UserSearch.php
@@ -0,0 +1,77 @@
+ $query,
+ ]);
+
+ $this->load($params);
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'id' => $this->id,
+ 'status' => $this->status,
+ 'created_at' => $this->created_at,
+ 'updated_at' => $this->updated_at,
+ ]);
+
+ $query->andFilterWhere(['like', 'username', $this->username])
+ ->andFilterWhere(['like', 'auth_key', $this->auth_key])
+ ->andFilterWhere(['like', 'password_hash', $this->password_hash])
+ ->andFilterWhere(['like', 'password_reset_token', $this->password_reset_token])
+ ->andFilterWhere(['like', 'email', $this->email])
+ ->andFilterWhere(['role' => $this->role]);
+
+ return $dataProvider;
+ }
+}
diff --git a/common/my_templates/crud/default/controller.php b/common/my_templates/crud/default/controller.php
new file mode 100644
index 0000000..e116fb5
--- /dev/null
+++ b/common/my_templates/crud/default/controller.php
@@ -0,0 +1,208 @@
+controllerClass);
+$modelClass = StringHelper::basename($generator->modelClass);
+$searchModelClass = StringHelper::basename($generator->modelClass); // prev : $generator->searchModelClass
+if ($modelClass === $searchModelClass) {
+ $searchModelAlias = $searchModelClass . 'Search';
+}
+
+/* @var $class ActiveRecordInterface */
+$class = $generator->modelClass;
+$pks = $class::primaryKey();
+$urlParams = $generator->generateUrlParams();
+$actionParams = $generator->generateActionParams();
+$actionParamComments = $generator->generateActionParamComments();
+
+echo "
+
+namespace = StringHelper::dirname(ltrim($generator->controllerClass, '\\')) ?>;
+
+use Yii;
+use common\models\User;
+
+use = ltrim($generator->modelClass, '\\') ?>;
+searchModelClass)): ?>
+// use = ltrim($generator->searchModelClass, '\\') . (isset($searchModelAlias) ? " as $searchModelAlias" : "") ?>;
+use common\models\search\= $searchModelAlias ?>;
+
+use yii\data\ActiveDataProvider;
+
+use = ltrim($generator->baseControllerClass, '\\') ?>;
+use yii\web\NotFoundHttpException;
+use yii\filters\VerbFilter;
+use yii\filters\AccessControl;
+
+/**
+ * = $controllerClass ?> implements the CRUD actions for = $modelClass ?> model.
+ */
+class = $controllerClass ?> extends = StringHelper::basename($generator->baseControllerClass) . "\n" ?>
+{
+ /**
+ * @inheritdoc
+ */
+ public function behaviors()
+ {
+ return [
+ 'access' => [
+ 'class' => AccessControl::className(),
+ 'rules' => [
+ [
+ 'actions' => ['index', 'view', 'edit', 'create', 'update', 'delete', 'multipledelete'],
+ 'allow' => true,
+ 'roles' => ['@'],
+ 'matchCallback' => function ($rule, $action)
+ {
+ return Yii::$app->user->identity['role'] == User::ROLE_ADMIN;
+ }
+ ],
+ ],
+ ],
+ 'verbs' => [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all = $modelClass ?> models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+searchModelClass)): ?>
+ $searchModel = new = isset($searchModelAlias) ? $searchModelAlias : $searchModelClass ?>();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+
+ $dataProvider = new ActiveDataProvider([
+ 'query' => = $modelClass ?>::find(),
+ ]);
+
+ return $this->render('index', [
+ 'dataProvider' => $dataProvider,
+ ]);
+
+ }
+
+ /**
+ * Displays a single = $modelClass ?> model.
+ * = implode("\n * ", $actionParamComments) . "\n" ?>
+ * @return mixed
+ */
+ public function actionView(= $actionParams ?>)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel(= $actionParams ?>),
+ ]);
+ }
+
+ /**
+ * Creates a new = $modelClass ?> model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new = $modelClass ?>();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', = $urlParams ?>]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing = $modelClass ?> model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * = implode("\n * ", $actionParamComments) . "\n" ?>
+ * @return mixed
+ */
+ public function actionUpdate(= $actionParams ?>)
+ {
+ $model = $this->findModel(= $actionParams ?>);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', = $urlParams ?>]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing = $modelClass ?> model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * = implode("\n * ", $actionParamComments) . "\n" ?>
+ * @return mixed
+ */
+ public function actionDelete(= $actionParams ?>)
+ {
+ $this->findModel(= $actionParams ?>)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Delete multiple IDs
+ * @return mixed
+ */
+ public function actionMultipledelete()
+ {
+ if (Yii::$app->request->isAjax)
+ {
+ $selected_ids = Yii::$app->request->post('selectedItems');
+ foreach ($selected_ids as $id)
+ $this->findModel($id)->delete();
+ }
+ }
+
+ /**
+ * Finds the = $modelClass ?> model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * = implode("\n * ", $actionParamComments) . "\n" ?>
+ * @return = $modelClass ?> the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel(= $actionParams ?>)
+ {
+ \$$pk";
+ }
+ $condition = '[' . implode(', ', $condition) . ']';
+}
+?>
+ if (($model = = $modelClass ?>::findOne(= $condition ?>)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/my_templates/crud/default/search.php b/common/my_templates/crud/default/search.php
new file mode 100644
index 0000000..88edbb1
--- /dev/null
+++ b/common/my_templates/crud/default/search.php
@@ -0,0 +1,87 @@
+modelClass);
+$searchModelClass = StringHelper::basename($generator->searchModelClass);
+if ($modelClass === $searchModelClass) {
+ $modelAlias = $modelClass . 'Model';
+}
+$rules = $generator->generateSearchRules();
+$labels = $generator->generateSearchLabels();
+$searchAttributes = $generator->getSearchAttributes();
+$searchConditions = $generator->generateSearchConditions();
+
+echo "
+
+namespace = StringHelper::dirname(ltrim($generator->searchModelClass, '\\')) ?>;
+
+use Yii;
+use yii\base\Model;
+use yii\data\ActiveDataProvider;
+use = ltrim($generator->modelClass, '\\') . (isset($modelAlias) ? " as $modelAlias" : "") ?>;
+
+/**
+ * = $searchModelClass ?> represents the model behind the search form about `= $generator->modelClass ?>`.
+ */
+class = $searchModelClass ?> extends = isset($modelAlias) ? $modelAlias : $modelClass ?>
+
+{
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ = implode(",\n ", $rules) ?>,
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function scenarios()
+ {
+ // bypass scenarios() implementation in the parent class
+ return Model::scenarios();
+ }
+
+ /**
+ * Creates data provider instance with search query applied
+ *
+ * @param array $params
+ *
+ * @return ActiveDataProvider
+ */
+ public function search($params)
+ {
+ $query = = isset($modelAlias) ? $modelAlias : $modelClass ?>::find();
+
+ // add conditions that should always apply here
+
+ $dataProvider = new ActiveDataProvider([
+ 'query' => $query,
+ ]);
+
+ $this->load($params);
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ // grid filtering conditions
+ = implode("\n ", $searchConditions) ?>
+
+ return $dataProvider;
+ }
+}
diff --git a/common/my_templates/crud/default/views/_form.php b/common/my_templates/crud/default/views/_form.php
new file mode 100644
index 0000000..e048b91
--- /dev/null
+++ b/common/my_templates/crud/default/views/_form.php
@@ -0,0 +1,43 @@
+modelClass();
+$safeAttributes = $model->safeAttributes();
+if (empty($safeAttributes)) {
+ $safeAttributes = $model->attributes();
+}
+
+echo "
+
+use yii\helpers\Html;
+use yii\widgets\ActiveForm;
+
+/* @var $this yii\web\View */
+/* @var $model = ltrim($generator->modelClass, '\\') ?> */
+/* @var $form yii\widgets\ActiveForm */
+?>
+
+
diff --git a/common/my_templates/crud/default/views/_search.php b/common/my_templates/crud/default/views/_search.php
new file mode 100644
index 0000000..fc45d2e
--- /dev/null
+++ b/common/my_templates/crud/default/views/_search.php
@@ -0,0 +1,44 @@
+
+
+use yii\helpers\Html;
+use yii\widgets\ActiveForm;
+
+/* @var $this yii\web\View */
+/* @var $model = ltrim($generator->searchModelClass, '\\') ?> */
+/* @var $form yii\widgets\ActiveForm */
+?>
+
+
+
+ = "$form = ActiveForm::begin([
+ 'action' => ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+getColumnNames() as $attribute) {
+ if (++$count < 6) {
+ echo " = " . $generator->generateActiveSearchField($attribute) . " ?>\n\n";
+ } else {
+ echo " generateActiveSearchField($attribute) . " ?>\n\n";
+ }
+}
+?>
+
+ = "= " ?>Html::submitButton(= $generator->generateString('Search') ?>, ['class' => 'btn btn-primary']) ?>
+ = "= " ?>Html::resetButton(= $generator->generateString('Reset') ?>, ['class' => 'btn btn-default']) ?>
+
+
+ = "ActiveForm::end(); ?>
+
+
diff --git a/common/my_templates/crud/default/views/create.php b/common/my_templates/crud/default/views/create.php
new file mode 100644
index 0000000..10b00b9
--- /dev/null
+++ b/common/my_templates/crud/default/views/create.php
@@ -0,0 +1,30 @@
+
+
+use yii\helpers\Html;
+
+
+/* @var $this yii\web\View */
+/* @var $model = ltrim($generator->modelClass, '\\') ?> */
+
+$this->title = = $generator->generateString('Create ' . Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>;
+$this->params['breadcrumbs'][] = ['label' => = $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= "= " ?>Html::encode($this->title) ?>
+
+ = "= " ?>$this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/my_templates/crud/default/views/index.php b/common/my_templates/crud/default/views/index.php
new file mode 100644
index 0000000..142b407
--- /dev/null
+++ b/common/my_templates/crud/default/views/index.php
@@ -0,0 +1,104 @@
+generateUrlParams();
+$nameAttribute = $generator->getNameAttribute();
+
+echo "
+
+use yii\helpers\Html;
+use = $generator->indexWidgetType === 'grid' ? "yii\\grid\\GridView" : "yii\\widgets\\ListView" ?>;
+= $generator->enablePjax ? 'use yii\widgets\Pjax;' : '' ?>
+
+/* @var $this yii\web\View */
+= !empty($generator->searchModelClass) ? "/* @var \$searchModel " . ltrim($generator->searchModelClass, '\\') . " */\n" : '' ?>
+/* @var $dataProvider yii\data\ActiveDataProvider */
+
+$this->title = = $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>;
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= "= " ?>Html::encode($this->title) ?>
+searchModelClass)): ?>
+= " indexWidgetType === 'grid' ? "// " : "") ?>echo $this->render('_search', ['model' => $searchModel]); ?>
+
+
+
+ = "= " ?>Html::a(= $generator->generateString('Create ' . Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>, ['create'], ['class' => 'btn btn-success']) ?>
+
+ Delete Selected
+
+= $generator->enablePjax ? ' ' : '' ?>
+
+indexWidgetType === 'grid'): ?>
+ = "= " ?>GridView::widget([
+ 'dataProvider' => $dataProvider,
+ = !empty($generator->searchModelClass) ? "'filterModel' => \$searchModel,\n 'columns' => [\n" : "'columns' => [\n"; ?>
+ [
+ 'class' => 'yii\grid\CheckboxColumn',
+ 'options' => ['width' => '32px'],
+ ],
+ ['class' => 'yii\grid\SerialColumn'],
+
+getTableSchema()) === false) {
+ foreach ($generator->getColumnNames() as $name) {
+ if (++$count < 6) {
+ if ($name == 'id')
+ {
+ echo " [\n";
+ echo " 'attribute' => 'id',\n";
+ echo " 'options' => ['width' => '70px'],\n";
+ echo " ],\n";
+ }
+ else
+ echo " '" . $name . "',\n";
+ } else {
+ echo " // '" . $name . "',\n";
+ }
+ }
+} else {
+ foreach ($tableSchema->columns as $column) {
+ $format = $generator->generateColumnFormat($column);
+ if (++$count < 6) {
+ if ($column->name == 'id')
+ {
+ echo " [\n";
+ echo " 'attribute' => 'id',\n";
+ echo " 'options' => ['width' => '70px'],\n";
+ echo " ],\n";
+ }
+ else
+ echo " '" . $column->name . ($format === 'text' ? "" : ":" . $format) . "',\n";
+ } else {
+ echo " // '" . $column->name . ($format === 'text' ? "" : ":" . $format) . "',\n";
+ }
+ }
+}
+?>
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
+ = "= " ?>ListView::widget([
+ 'dataProvider' => $dataProvider,
+ 'itemOptions' => ['class' => 'item'],
+ 'itemView' => function ($model, $key, $index, $widget) {
+ return Html::a(Html::encode($model->= $nameAttribute ?>), ['view', = $urlParams ?>]);
+ },
+ ]) ?>
+
+= $generator->enablePjax ? ' ' : '' ?>
+
+
+
+='='?> $this->render('//layouts/js/_multipledelete_script');?>
diff --git a/common/my_templates/crud/default/views/update.php b/common/my_templates/crud/default/views/update.php
new file mode 100644
index 0000000..0849e3a
--- /dev/null
+++ b/common/my_templates/crud/default/views/update.php
@@ -0,0 +1,32 @@
+generateUrlParams();
+
+echo "
+
+use yii\helpers\Html;
+
+/* @var $this yii\web\View */
+/* @var $model = ltrim($generator->modelClass, '\\') ?> */
+
+$this->title = = $generator->generateString('Update {modelClass}: ', ['modelClass' => Inflector::camel2words(StringHelper::basename($generator->modelClass))]) ?> . $model->= in_array('label', $generator->getColumnNames()) ? 'label' : $generator->getNameAttribute() ?>;
+$this->params['breadcrumbs'][] = ['label' => = $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->= in_array('label', $generator->getColumnNames()) ? 'label' : $generator->getNameAttribute() ?>, 'url' => ['view', = $urlParams ?>]];
+$this->params['breadcrumbs'][] = = $generator->generateString('Update') ?>;
+?>
+
+
+
= "= " ?>Html::encode($this->title) ?>
+
+ = "= " ?>$this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/my_templates/crud/default/views/view.php b/common/my_templates/crud/default/views/view.php
new file mode 100644
index 0000000..b436bec
--- /dev/null
+++ b/common/my_templates/crud/default/views/view.php
@@ -0,0 +1,65 @@
+generateUrlParams();
+
+echo "
+
+use yii\helpers\Html;
+use yii\widgets\DetailView;
+
+/* @var $this yii\web\View */
+/* @var $model = ltrim($generator->modelClass, '\\') ?> */
+
+$this->title = $model->= in_array('label', $generator->getColumnNames()) ? 'label' : $generator->getNameAttribute() ?>;
+$this->params['breadcrumbs'][] = ['label' => = $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= "= " ?>Html::encode($this->title) ?>
+
+
+ = "= " ?>Html::a(= $generator->generateString('Update') ?>, ['update', = $urlParams ?>], ['class' => 'btn btn-primary']) ?>
+ = "= " ?>Html::a(= $generator->generateString('Delete') ?>, ['delete', = $urlParams ?>], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => = $generator->generateString('Are you sure you want to delete this item?') ?>,
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = "= " ?>DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+getTableSchema()) === false) {
+ foreach ($generator->getColumnNames() as $name) {
+ echo " '" . $name . "',\n";
+ }
+} else {
+ foreach ($generator->getTableSchema()->columns as $column) {
+ $format = $generator->generateColumnFormat($column);
+ if ($column->name == 'created_at' || $column->name == 'updated_at')
+ {
+ echo " [\n";
+ echo " 'attribute' => '" . $column->name . "',\n";
+ echo " 'value' => date('Y-m-d H:i:s', \$model->" . $column->name . ")\n";
+ echo " ],\n";
+ }
+ else
+ echo " '" . $column->name . ($format === 'text' ? "" : ":" . $format) . "',\n";
+ }
+}
+?>
+ ],
+ ]) ?>
+
+
diff --git a/composer.json b/composer.json
index 9ca5f9c..de6396e 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,8 @@
"php": ">=5.4.0",
"yiisoft/yii2": ">=2.0.6",
"yiisoft/yii2-bootstrap": "*",
- "yiisoft/yii2-swiftmailer": "*"
+ "yiisoft/yii2-swiftmailer": "*",
+ "creocoder/yii2-nested-sets": "^0.9.0"
},
"require-dev": {
"yiisoft/yii2-codeception": "*",
diff --git a/composer.lock b/composer.lock
index a0e9104..352916f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "914f9194a4d021de88b285b03e683984",
- "content-hash": "e1929a97e872bdbaacc8530468eccb67",
+ "hash": "cdd3966c677634ee1a9b9cfa9ad22b73",
+ "content-hash": "ecb439eaefbb1ccddd62962bdc2849bb",
"packages": [
{
"name": "bower-asset/bootstrap",
@@ -58,16 +58,16 @@
},
{
"name": "bower-asset/jquery",
- "version": "2.2.3",
+ "version": "2.2.4",
"source": {
"type": "git",
"url": "https://github.com/jquery/jquery-dist.git",
- "reference": "af22a351b2ea5801ffb1695abb3bb34d5bed9198"
+ "reference": "c0185ab7c75aab88762c5aae780b9d83b80eda72"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/af22a351b2ea5801ffb1695abb3bb34d5bed9198",
- "reference": "af22a351b2ea5801ffb1695abb3bb34d5bed9198",
+ "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/c0185ab7c75aab88762c5aae780b9d83b80eda72",
+ "reference": "c0185ab7c75aab88762c5aae780b9d83b80eda72",
"shasum": ""
},
"type": "bower-asset-library",
@@ -255,6 +255,46 @@
],
"time": "2015-03-06 05:28:07"
},
+ {
+ "name": "creocoder/yii2-nested-sets",
+ "version": "0.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/creocoder/yii2-nested-sets.git",
+ "reference": "cb8635a459b6246e5a144f096b992dcc30cf9954"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/creocoder/yii2-nested-sets/zipball/cb8635a459b6246e5a144f096b992dcc30cf9954",
+ "reference": "cb8635a459b6246e5a144f096b992dcc30cf9954",
+ "shasum": ""
+ },
+ "require": {
+ "yiisoft/yii2": "*"
+ },
+ "type": "yii2-extension",
+ "autoload": {
+ "psr-4": {
+ "creocoder\\nestedsets\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Alexander Kochetov",
+ "email": "creocoder@gmail.com"
+ }
+ ],
+ "description": "The nested sets behavior for the Yii framework",
+ "keywords": [
+ "nested sets",
+ "yii2"
+ ],
+ "time": "2015-01-27 10:53:51"
+ },
{
"name": "ezyang/htmlpurifier",
"version": "v4.7.0",
@@ -301,23 +341,23 @@
},
{
"name": "swiftmailer/swiftmailer",
- "version": "v5.4.2",
+ "version": "v5.4.3",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
- "reference": "d8db871a54619458a805229a057ea2af33c753e8"
+ "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d8db871a54619458a805229a057ea2af33c753e8",
- "reference": "d8db871a54619458a805229a057ea2af33c753e8",
+ "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
+ "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
- "mockery/mockery": "~0.9.1,<0.9.4"
+ "mockery/mockery": "~0.9.1"
},
"type": "library",
"extra": {
@@ -350,24 +390,24 @@
"mail",
"mailer"
],
- "time": "2016-05-01 08:45:47"
+ "time": "2016-07-08 11:51:25"
},
{
"name": "yiisoft/yii2",
- "version": "2.0.8",
+ "version": "2.0.9",
"source": {
"type": "git",
"url": "https://github.com/yiisoft/yii2-framework.git",
- "reference": "53992b136b993e32ca7b6f399cf42b143f8685a6"
+ "reference": "2b75151ea60e1fd820046416eee2e89c3dda1133"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/53992b136b993e32ca7b6f399cf42b143f8685a6",
- "reference": "53992b136b993e32ca7b6f399cf42b143f8685a6",
+ "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2b75151ea60e1fd820046416eee2e89c3dda1133",
+ "reference": "2b75151ea60e1fd820046416eee2e89c3dda1133",
"shasum": ""
},
"require": {
- "bower-asset/jquery": "2.2.*@stable | 2.1.*@stable | 1.11.*@stable",
+ "bower-asset/jquery": "2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable",
"bower-asset/jquery.inputmask": "~3.2.2",
"bower-asset/punycode": "1.3.*",
"bower-asset/yii2-pjax": "~2.0.1",
@@ -444,7 +484,7 @@
"framework",
"yii2"
],
- "time": "2016-04-28 14:50:20"
+ "time": "2016-07-11 13:36:42"
},
{
"name": "yiisoft/yii2-bootstrap",
diff --git a/frontend/models/SignupForm.php b/frontend/models/SignupForm.php
index f15f3a8..971294c 100644
--- a/frontend/models/SignupForm.php
+++ b/frontend/models/SignupForm.php
@@ -51,6 +51,7 @@ public function signup()
$user->username = $this->username;
$user->email = $this->email;
$user->setPassword($this->password);
+ $user->role = User::ROLE_USER;
$user->generateAuthKey();
return $user->save() ? $user : null;
diff --git a/frontend/views/site/login.php b/frontend/views/site/login.php
index 56ea98e..90e81c4 100644
--- a/frontend/views/site/login.php
+++ b/frontend/views/site/login.php
@@ -19,7 +19,7 @@
'login-form']); ?>
- = $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
+ = $form->field($model, 'email')->textInput(['autofocus' => true]) ?>
= $form->field($model, 'password')->passwordInput() ?>
diff --git a/frontend/web/.htaccess b/frontend/web/.htaccess
new file mode 100644
index 0000000..50fc2ac
--- /dev/null
+++ b/frontend/web/.htaccess
@@ -0,0 +1,12 @@
+Options +FollowSymLinks
+IndexIgnore */*
+
+RewriteEngine on
+
+# if a directory or a file exists, use it directly
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+
+# otherwise forward it to index.php
+RewriteRule . index.php
+