diff --git a/components/Controller.php b/components/Controller.php index 88804a13..d11a858d 100644 --- a/components/Controller.php +++ b/components/Controller.php @@ -13,6 +13,20 @@ class Controller extends yii\web\Controller { + public $uid = null; + + /** + * @param \yii\base\Action $action + * @return bool + */ + public function beforeAction($action) { + parent::beforeAction($action); + if (Yii::$app->user->id) { + $this->uid = Yii::$app->user->id; + } + return true; + } + /** * json渲染. PS:调用此方法之前若有输出将会出错 * diff --git a/components/GlobalHelper.php b/components/GlobalHelper.php index e95bbee6..428767e5 100644 --- a/components/GlobalHelper.php +++ b/components/GlobalHelper.php @@ -9,6 +9,7 @@ namespace app\components; +use app\models\User; use yii; class GlobalHelper { @@ -54,4 +55,8 @@ public static function convert2Utf8($text) { return $out; } + + public static function formatAvatar($pic) { + return rtrim(User::AVATAR_ROOT, '/') . '/' . $pic; + } } diff --git a/config/params.php b/config/params.php index c9aa34a2..7aa6c956 100644 --- a/config/params.php +++ b/config/params.php @@ -1,18 +1,26 @@ 'admin@huamanshu.com', - 'support.name' => 'admin', + // ******必须要与config/web.php 中mail模块的username一致***** + 'support.email' => 'service@huamanshu.com', + // 显示发件人的名字,可以随意 + 'support.name' => 'service', 'user.passwordResetTokenExpire' => 3600, - 'user.emailConfirmationTokenExpire' => 43200, // 5 days - - // 操作日志目录 + 'user.emailConfirmationTokenExpire' => 43200, // 5 days有效 + + // 头像图片后缀 + 'user.avatar.extension' => [ + 'jpg', 'png', 'jpeg', + ], + + // *******操作日志目录******* 'log.dir' => '/tmp/walle/', - // 指定邮箱后缀 + // *******指定公司邮箱后缀******* 'mail-suffix' => [ - '公司邮箱后缀.com', // 限制只有公司同学可注册,可多个 - ] + 'huamanshu.com', # 支持多个 + ], ]; diff --git a/config/web.php b/config/web.php index aabba738..a4537d71 100644 --- a/config/web.php +++ b/config/web.php @@ -27,11 +27,11 @@ 'useFileTransport' => false, 'transport' => [ 'class' => 'Swift_SmtpTransport', - 'host' => 'ip or host', # smtp 发件地址 - 'username' => 'admin@huamanshu.com', # smtp 发件用户名 - 'password' => 'password', # smtp 发件人的密码 - 'port' => 25, # smtp 端口 - 'encryption' => 'tls', # smtp 协议 + 'host' => 'smtp.exmail.qq.com', # smtp 发件地址 + 'username' => 'service@huamanshu.com', # smtp 发件用户名 + 'password' => 'K84erUuxg1bHqrfD', # smtp 发件人的密码 + 'port' => 25, # smtp 端口 + 'encryption' => 'tls', # smtp 协议 ], 'messageConfig' => [ 'charset' => 'UTF-8', diff --git a/controllers/ConfController.php b/controllers/ConfController.php index e269c422..a0a3a878 100644 --- a/controllers/ConfController.php +++ b/controllers/ConfController.php @@ -7,10 +7,23 @@ use app\components\Controller; use app\models\Conf; use app\models\User; +use app\models\Group; class ConfController extends Controller { + /** + * @param \yii\base\Action $action + * @return bool + */ + public function beforeAction($action) { + parent::beforeAction($action); + if (\Yii::$app->user->identity->role != User::ROLE_ADMIN) { + throw new \Exception('非管理员不能操作:('); + } + return true; + } + /** * 配置项目列表 * @@ -27,6 +40,62 @@ public function actionIndex() { ]); } + /** + * 配置项目 + * + * @param $projectId + * @return string + * @throws \Exception + */ + public function actionPreview($projectId) { + $this->layout = 'modal'; + $conf = Conf::findOne($projectId); + if (!$conf) throw new \Exception('找不到项目'); + + return $this->render('preview', [ + 'conf' => $conf, + ]); + } + + /** + * 配置项目 + * + * @param $projectId + * @return string + * @throws \Exception + */ + public function actionGroup($projectId) { + // 配置信息 + $conf = Conf::findOne($projectId); + if (!$conf) { + throw new \Exception('项目不存在:)'); + } + if ($conf->user_id != $this->uid) { + throw new \Exception('不可以操作其它人的项目:)'); + } + // 添加用户 + if (\Yii::$app->request->getIsPost()) { + Group::addGroupUser($projectId, \Yii::$app->request->post('user')); + } + // 项目的分组用户 + $group = Group::find() + ->with('user') + ->where(['project_id' => $projectId]) + ->indexBy('user_id') + ->asArray()->all(); + // 所有用户 + $users = User::find() + ->select(['id', 'email', 'realname']) + ->where(['is_email_verified' => 1]) + ->asArray()->all(); + + return $this->render('group', [ + 'conf' => $conf, + 'users' => $users, + 'group' => $group, + ]); + } + /** * 配置项目 * @@ -35,10 +104,9 @@ public function actionIndex() { * @throws \Exception */ public function actionEdit($projectId = null) { - if (\Yii::$app->user->identity->role != User::ROLE_ADMIN) throw new \Exception('非管理员不能操作:('); $conf = $projectId ? Conf::findOne($projectId) : new Conf(); if (\Yii::$app->request->getIsPost() && $conf->load(Yii::$app->request->post())) { - $conf->user_id = \Yii::$app->user->id; + $conf->user_id = $this->uid; if ($conf->save()) { $this->redirect('/conf/'); } @@ -56,16 +124,35 @@ public function actionEdit($projectId = null) { * @return string * @throws \Exception */ - public function actionDelete($confId) { - $conf = Conf::findOne($confId); + public function actionDelete($projectId) { + $conf = Conf::findOne($projectId); if (!$conf) { throw new \Exception('项目不存在:)'); } - if ($conf->user_id != \Yii::$app->user->id) { + if ($conf->user_id != $this->uid) { throw new \Exception('不可以操作其它人的项目:)'); } if (!$conf->delete()) throw new \Exception('删除失败'); $this->renderJson([]); + } + /** + * 删除项目的用户关系 + * + * @return string + * @throws \Exception + */ + public function actionDeleteRelation($id) { + $group = Group::findOne($id); + if (!$group) { + throw new \Exception('关系不存在:)'); + } + $conf = Conf::findOne($group->project_id); + if ($conf->user_id != $this->uid) { + throw new \Exception('不可以操作其它人的项目:)'); + } + + if (!$group->delete()) throw new \Exception('删除失败'); + $this->renderJson([]); } } diff --git a/controllers/TaskController.php b/controllers/TaskController.php index b8f9b79d..0ce59da3 100644 --- a/controllers/TaskController.php +++ b/controllers/TaskController.php @@ -8,6 +8,7 @@ use app\models\Task; use app\models\Conf; use app\models\User; +use app\models\Group; class TaskController extends Controller { @@ -15,12 +16,11 @@ class TaskController extends Controller { public function actionIndex($page = 1, $size = 10) { $size = $this->getParam('per-page') ?: $size; - $user = User::findOne(\Yii::$app->user->id); $list = Task::find() ->with('user') ->with('conf'); - if ($user->role != User::ROLE_ADMIN) { - $list->where(['user_id' => \Yii::$app->user->id]); + if (\Yii::$app->user->identity->role != User::ROLE_ADMIN) { + $list->where(['user_id' => $this->uid]); } $kw = \Yii::$app->request->post('kw'); @@ -31,7 +31,7 @@ public function actionIndex($page = 1, $size = 10) { $list = $tasks->offset(($page - 1) * $size)->limit(10) ->asArray()->all(); - $view = $user->role == User::ROLE_ADMIN ? 'admin-list' : 'dev-list'; + $view = \Yii::$app->user->identity->role == User::ROLE_ADMIN ? 'admin-list' : 'dev-list'; $pages = new Pagination(['totalCount' => $tasks->count(), 'pageSize' => 10]); return $this->render($view, [ 'list' => $list, @@ -53,14 +53,22 @@ public function actionSubmit($projectId = null) { ->where(['id' => $projectId, 'status' => Conf::STATUS_VALID]) ->one(); } - if (\Yii::$app->request->getIsPost() && $conf && $task->load(\Yii::$app->request->post())) { - // 是否需要审核 - $status = $conf->audit == Conf::AUDIT_YES ? Task::STATUS_SUBMIT : Task::STATUS_PASS; - $task->user_id = \Yii::$app->user->id; - $task->project_id = $projectId; - $task->status = $status; - if ($task->save()) { - $this->redirect('/task/'); + if (\Yii::$app->request->getIsPost()) { + if (!$conf) throw new \Exception('未知的项目,请确认:)'); + $group = Group::find() + ->where(['user_id' => $this->uid, 'project_id' => $projectId]) + ->count(); + if (!$group) throw new \Exception('非该项目成员,无权限'); + + if ($task->load(\Yii::$app->request->post())) { + // 是否需要审核 + $status = $conf->audit == Conf::AUDIT_YES ? Task::STATUS_SUBMIT : Task::STATUS_PASS; + $task->user_id = $this->uid; + $task->project_id = $projectId; + $task->status = $status; + if ($task->save()) { + $this->redirect('/task/'); + } } } if ($projectId) { @@ -69,8 +77,10 @@ public function actionSubmit($projectId = null) { 'conf' => $conf, ]); } + // 成员所属项目 $projects = Conf::find() - ->where(['status' => Conf::STATUS_VALID]) + ->leftJoin(Group::tableName(), '`group`.project_id=conf.id') + ->where(['conf.status' => Conf::STATUS_VALID, '`group`.user_id' => $this->uid]) ->asArray()->all(); return $this->render('select-project', [ 'projects' => $projects, @@ -89,7 +99,7 @@ public function actionDelete($taskId) { if (!$task) { throw new \Exception('任务号不存在:)'); } - if ($task->user_id != \Yii::$app->user->id) { + if ($task->user_id != $this->uid) { throw new \Exception('不可以操作其它人的任务:)'); } if ($task->status == Task::STATUS_DONE) { @@ -111,7 +121,7 @@ public function actionRollback($taskId) { if (!$this->task) { throw new \Exception('任务号不存在:)'); } - if ($this->task->user_id != \Yii::$app->user->id) { + if ($this->task->user_id != $this->uid) { throw new \Exception('不可以操作其它人的任务:)'); } if ($this->task->ex_link_id == $this->task->link_id) { @@ -129,7 +139,7 @@ public function actionRollback($taskId) { $rollbackTask = new Task(); $rollbackTask->attributes = [ - 'user_id' => \Yii::$app->user->id, + 'user_id' => $this->uid, 'project_id' => $this->task->project_id, 'status' => $status, 'action' => Task::ACTION_ROLLBACK, diff --git a/controllers/UserController.php b/controllers/UserController.php new file mode 100644 index 00000000..61b74240 --- /dev/null +++ b/controllers/UserController.php @@ -0,0 +1,46 @@ +uid); + return $this->render('index', [ + 'user' => $user, + ]); + } + + public function actionAvatar() { + $fileParts = pathinfo($_FILES['avatar']['name']); + if ($_FILES['avatar']['error']) { + $this->renderJson([], -1, '上传附件失败'); + } + if ($_FILES['avatar']['size'] > static::AVATAR_SIZE) { + $this->renderJson([], -1, '文件过大'); + } + if (!in_array(strtolower($fileParts['extension']), \Yii::$app->params['user.avatar.extension'])) { + $this->renderJson([], -1, '上传附件失败,附件格式只支持:' . join(', ', \Yii::$app->params['user.avatar.extension'])); + } + $tempFile = $_FILES['avatar']['tmp_name']; + $baseName = sprintf('%s-%d.%s', date("YmdHis", time()), rand(10, 99), $fileParts['extension']); + $newFile = GlobalHelper::formatAvatar($baseName); + $targetFile = sprintf("%s/web/%s", rtrim(\Yii::$app->basePath, '/'), ltrim($newFile, '/')); + $ret = move_uploaded_file($tempFile, $targetFile); + if ($ret) { + $user = User::findOne($this->uid); + $user->avatar = $baseName; + $ret = $user->save(); + } + + $this->renderJson(['url' => $newFile], !$ret, $ret ?: '更新头像失败'); + } + +} diff --git a/controllers/WalleController.php b/controllers/WalleController.php index e20646fa..bc71e133 100644 --- a/controllers/WalleController.php +++ b/controllers/WalleController.php @@ -35,14 +35,6 @@ class WalleController extends Controller { public $enableCsrfValidation = false; - /** - * @param \yii\base\Action $action - * @return bool - */ - public function beforeAction($action) { - return parent::beforeAction($action); - } - /** * 发起上线 @@ -58,7 +50,7 @@ public function actionStartDeploy() { if (!$this->task) { throw new \Exception('任务号不存在:)'); } - if ($this->task->user_id != \Yii::$app->user->id) { + if ($this->task->user_id != $this->uid) { throw new \Exception('不可以操作其它人的任务:)'); } // 任务失败或者审核通过时可发起上线 @@ -182,7 +174,7 @@ public function actionDeploy($taskId) { if (!$this->task) { throw new \Exception('任务号不存在:)'); } - if ($this->task->user_id != \Yii::$app->user->id) { + if ($this->task->user_id != $this->uid) { throw new \Exception('不可以操作其它人的任务:)'); } diff --git a/mail/confirmEmail.php b/mail/confirmEmail.php index e4826d1a..53799611 100644 --- a/mail/confirmEmail.php +++ b/mail/confirmEmail.php @@ -8,5 +8,10 @@ $confirmationLink = Yii::$app->urlManager->createAbsoluteUrl(['site/confirm-email', 'token' => $user->email_confirmation_token]); ?> -点击完成注册 - +亲爱的realname ?>: + +

+点击以下链接完成注册,如不是本人操作,请忽略,谢谢! + +

+

diff --git a/mail/passwordResetToken.php b/mail/passwordResetToken.php index 5593f840..2d65d1c8 100644 --- a/mail/passwordResetToken.php +++ b/mail/passwordResetToken.php @@ -8,9 +8,11 @@ $resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); ?> +亲爱的realname ?>: -Hello username) ?>, +

+点击以下链接完成重置密码,如不是本人操作,请忽略,谢谢! -重置密码: +

+

- diff --git a/migrations/m150929_115951_project_user_group.php b/migrations/m150929_115951_project_user_group.php new file mode 100644 index 00000000..d5459d36 --- /dev/null +++ b/migrations/m150929_115951_project_user_group.php @@ -0,0 +1,37 @@ +createTable('group', [ + 'id' => Schema::TYPE_PK, + 'project_id' => Schema::TYPE_INTEGER . '(2) unsigned NOT NULL COMMENT "项目id"', + 'user_id' => Schema::TYPE_STRING . '(32) NOT NULL COMMENT "用户id"', + ]); + // 添加头像 + $this->addColumn(\app\models\User::tableName(), 'avatar', Schema::TYPE_STRING . '(100) DEFAULT "default.jpg" COMMENT "头像图片地址" AFTER email'); + } + + public function down() + { + $this->dropTable('group'); + $this->dropColumn(\app\models\User::tableName(), 'avatar'); + + return false; + } + + /* + // Use safeUp/safeDown to run migration code within a transaction + public function safeUp() + { + } + + public function safeDown() + { + } + */ +} diff --git a/models/Group.php b/models/Group.php new file mode 100644 index 00000000..272af24f --- /dev/null +++ b/models/Group.php @@ -0,0 +1,79 @@ + 32], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'project_id' => 'Project ID', + 'user_id' => 'User ID', + ]; + } + + /** + * width('user') + * + * @return \yii\db\ActiveQuery + */ + public function getUser() { + return $this->hasOne(User::className(), ['id' => 'user_id']); + } + + /** + * 项目添加用户 + * + * @param $projectId + * @param $userId + * @return bool + */ + public static function addGroupUser($projectId, $userId) { + // 是否已在组内 + $exists = Group::find() + ->where(['project_id' => $projectId, 'user_id' => $userId]) + ->count(); + if ($exists) return true; + + $group = new Group(); + $group->attributes = [ + 'project_id' => $projectId, + 'user_id' => $userId, + ]; + return $group->save(); + } + +} diff --git a/models/User.php b/models/User.php index 60be1f6a..f037a7a7 100644 --- a/models/User.php +++ b/models/User.php @@ -18,6 +18,7 @@ * @property string $password_reset_token * @property string $email_confirmation_token * @property string $email + * @property string $avatar * @property string $auth_key * @property integer $role * @property integer $status @@ -40,6 +41,12 @@ class User extends ActiveRecord implements IdentityInterface * 开发者 */ const ROLE_DEV = 2; + + /** + * 头像目录 + */ + const AVATAR_ROOT = '/dist/avatars/'; + /** * @var string|null the current password value from form input */ @@ -93,11 +100,11 @@ public function rules() ['username', 'filter', 'filter' => 'trim'], ['username', 'unique'], ['username', 'string', 'min' => 2, 'max' => 255], - - ['email', 'filter', 'filter' => 'trim'], - ['email', 'validateEmail'], - ['email', 'email'], - ['email', 'unique'], + [['avatar', 'realname'], 'string'], + [['email', 'avatar'], 'filter', 'filter' => 'trim'], + ['email', 'validateEmail', 'on'=>'signup'], + ['email', 'email', 'on'=>'signup'], + ['email', 'unique', 'on'=>'signup'], ]; } @@ -128,10 +135,10 @@ public function beforeSave($insert) if ($this->isNewRecord) { $this->generateAuthKey(); $this->generateEmailConfirmationToken(); + // 名字与邮箱 + $this->realname = $this->username; + $this->username = $this->email; } - $this->realname = $this->username; - $this->username = $this->email; - return parent::beforeSave($insert); } diff --git a/views/conf/edit.php b/views/conf/edit.php index 75cfc569..b2a22857 100644 --- a/views/conf/edit.php +++ b/views/conf/edit.php @@ -9,7 +9,7 @@ ?>
'login-form']); ?> -
+
field($conf, 'name') ->label('项目名字', ['class' => 'control-label bolder blue']) ?> field($conf, 'level')->dropDownList([ diff --git a/views/conf/group.php b/views/conf/group.php new file mode 100644 index 00000000..4fbb2c8b --- /dev/null +++ b/views/conf/group.php @@ -0,0 +1,106 @@ +title = '项目成员管理'; + +use yii\widgets\ActiveForm; +use app\models\User; + +?> +
+ + + +
+ 'login-form']); ?> +
+ + + + +
+ end() ?> +
+
+ +
+ +
+ +
+
+
+ + Bob Doe's avatar + +
+ +
+ +
+ +
+
+ +
+
+
+
+
+
+
+
+
+ + +
+ + \ No newline at end of file diff --git a/views/conf/index.php b/views/conf/index.php index 836f2810..0a56670e 100644 --- a/views/conf/index.php +++ b/views/conf/index.php @@ -45,11 +45,13 @@ class="btn btn-default btn-sm"> - + + + + + + -
\ No newline at end of file diff --git a/views/conf/preview.php b/views/conf/preview.php new file mode 100644 index 00000000..a648a00a --- /dev/null +++ b/views/conf/preview.php @@ -0,0 +1,141 @@ +title = $conf->name . '配置项目'; + +use yii\widgets\ActiveForm; +?> + + \ No newline at end of file diff --git a/views/layouts/main.php b/views/layouts/main.php index f2b6430d..0fe991f9 100644 --- a/views/layouts/main.php +++ b/views/layouts/main.php @@ -24,6 +24,7 @@ + @@ -88,7 +89,7 @@
  • - + 个人资料 @@ -203,6 +204,7 @@ + @@ -212,9 +214,24 @@ + endBody() ?> + endPage() ?> + diff --git a/views/layouts/modal.php b/views/layouts/modal.php new file mode 100644 index 00000000..fc50eef3 --- /dev/null +++ b/views/layouts/modal.php @@ -0,0 +1,113 @@ +user->id); +?> +beginPage() ?> + + + + + <?= Html::encode($this->title) ?> - Walle 瓦力平台 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + endBody() ?> + + + +endPage() ?> + + diff --git a/views/user/index.php b/views/user/index.php new file mode 100644 index 00000000..2d771cb7 --- /dev/null +++ b/views/user/index.php @@ -0,0 +1,264 @@ +title = '个人信息'; +use \app\components\GlobalHelper; + +?> +
    +
    +
    +
    + + Alex's Avatar + + + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    +
    + +
    +
    + + + + \ No newline at end of file diff --git a/web/dist/avatars/default.jpg b/web/dist/avatars/default.jpg new file mode 100644 index 00000000..cc9092f2 Binary files /dev/null and b/web/dist/avatars/default.jpg differ diff --git a/web/dist/css/walle.css b/web/dist/css/walle.css index 54fc216e..817cd4bd 100644 --- a/web/dist/css/walle.css +++ b/web/dist/css/walle.css @@ -1,3 +1,13 @@ .tip { display: none; +} +.profile-users .memberdiv .popover { + min-width: 200px; +} + +.profile-users .memberdiv .popover-content { + min-width: 200px; +} +.show { + display: block; } \ No newline at end of file