Browse Source

User Profile

Dashboard fix
Blog post published date compare
master
Egorka 6 years ago
parent
commit
c23a8831cf
  1. 5
      README.md
  2. 34
      backend/controllers/UserController.php
  3. 1
      backend/messages/ru/main.php
  4. 5
      backend/messages/ru/user.php
  5. 31
      backend/views/layouts/header.php
  6. 2
      backend/views/layouts/left.php
  7. 59
      backend/views/user/profile.php
  8. 2
      common/config/main.php
  9. 5
      common/modules/blog/entities/queries/BlogPostQuery.php
  10. 6
      common/modules/blog/forms/BlogPostForm.php
  11. 2
      common/modules/blog/messages/ru/blog.php
  12. 12
      common/modules/blog/repositories/read/BlogPostReadRepository.php
  13. 42
      common/modules/blog/views/manage/post/_form.php
  14. 171
      core/components/avatar_generator/AvatarGenerator.php
  15. BIN
      core/components/avatar_generator/Play-Bold.ttf
  16. 13
      core/entities/user/User.php
  17. 8
      core/forms/user/ProfileEditForm.php
  18. 2
      core/services/user/ProfileService.php
  19. 81
      docs/develope/Modules.md
  20. 18
      docs/develope/README.md

5
README.md

@ -1,7 +1,4 @@
<p align="center"> # ZxCMS - Yii2 site management system
<h1 align="center">ZxCMS - Yii2 site management system</h1>
<br>
</p>
Yii 2 Advanced Project Template is a skeleton [Yii 2](http://www.yiiframework.com/) application best for Yii 2 Advanced Project Template is a skeleton [Yii 2](http://www.yiiframework.com/) application best for
developing complex Web applications with multiple tiers. developing complex Web applications with multiple tiers.

34
backend/controllers/UserController.php

@ -2,7 +2,9 @@
namespace backend\controllers; namespace backend\controllers;
use core\forms\user\ProfileEditForm;
use core\forms\user\UserForm; use core\forms\user\UserForm;
use core\services\user\ProfileService;
use core\services\user\UserManageService; use core\services\user\UserManageService;
use Yii; use Yii;
use core\entities\user\User; use core\entities\user\User;
@ -11,6 +13,7 @@ use yii\web\Controller;
use yii\web\NotFoundHttpException; use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter; use yii\filters\VerbFilter;
use yii\filters\AccessControl; use yii\filters\AccessControl;
use yii\web\UploadedFile;
/** /**
* UserController implements the CRUD actions for User model. * UserController implements the CRUD actions for User model.
@ -18,11 +21,13 @@ use yii\filters\AccessControl;
class UserController extends Controller class UserController extends Controller
{ {
private $service; private $service;
private $profile_service;
public function __construct($id, $module, UserManageService $service, $config = []) public function __construct($id, $module, UserManageService $service, ProfileService $profile_service, $config = [])
{ {
parent::__construct($id, $module, $config); parent::__construct($id, $module, $config);
$this->service = $service; $this->service = $service;
$this->profile_service = $profile_service;
} }
/** /**
@ -39,6 +44,11 @@ class UserController extends Controller
'allow' => true, 'allow' => true,
'roles' => ['UserManagement'], 'roles' => ['UserManagement'],
], ],
[
'actions' => ['profile'],
'allow' => true,
'roles' => ['@'],
],
[ // all the action are accessible to admin [ // all the action are accessible to admin
'allow' => true, 'allow' => true,
'roles' => ['admin'], 'roles' => ['admin'],
@ -142,6 +152,28 @@ class UserController extends Controller
return $this->redirect(['index']); return $this->redirect(['index']);
} }
public function actionProfile()
{
$user = $this->findModel(Yii::$app->user->id);
$form = new ProfileEditForm($user);
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$form->user_pic = UploadedFile::getInstance($form, 'user_pic');
$this->profile_service->edit(Yii::$app->user->id, $form);
Yii::$app->session->setFlash('success', Yii::t('user', 'Profile is saved.'));
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('profile', [
'model' => $form,
'user' => $user,
]);
}
/** /**
* Finds the User model based on its primary key value. * Finds the User model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown. * If the model is not found, a 404 HTTP exception will be thrown.

1
backend/messages/ru/main.php

@ -26,4 +26,5 @@ return [
'Change at your own risk' => 'Редактируйте на свой страх и риск', 'Change at your own risk' => 'Редактируйте на свой страх и риск',
'Online' => 'В сети', 'Online' => 'В сети',
'Search results' => 'Поиск', 'Search results' => 'Поиск',
'Search...' => 'Поиск...',
]; ];

5
backend/messages/ru/user.php

@ -32,4 +32,9 @@ return [
'Update Permission: {permission}' => 'Редактирование разрешения: {permission}', 'Update Permission: {permission}' => 'Редактирование разрешения: {permission}',
'Administrator has full access rules' => 'Администратор обладает всеми правами доступа', 'Administrator has full access rules' => 'Администратор обладает всеми правами доступа',
'Children roles and permissions for "{role}" is updated.' => 'Дочерние роли и разрешения для роли "{role}" обновлены.', 'Children roles and permissions for "{role}" is updated.' => 'Дочерние роли и разрешения для роли "{role}" обновлены.',
'Profile' => 'Профиль',
'Profile is saved.' => 'Профиль обновлен.',
'Profile: {username}' => 'Профиль: {username}',
'User Picture' => 'Аватар',
'Registered: {date}' => 'Дата регистрации: {date}',
]; ];

31
backend/views/layouts/header.php

@ -232,36 +232,28 @@ use core\components\avatar_generator\AvatarGenerator;
<li class="dropdown user user-menu"> <li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="<?= Yii::$app->avatar->show(Yii::$app->user->identity->user->username) ?>" class="user-image" alt="User Image"/> <img src="<?= Yii::$app->avatar->show(Yii::$app->user->identity->user->username) ?>" class="user-image" alt="<?= Yii::$app->user->identity->user->username ?>"/>
<span class="hidden-xs"><?= Yii::$app->user->identity->user->username ?></span> <span class="hidden-xs"><?= Yii::$app->user->identity->user->username ?></span>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<!-- User image --> <!-- User image -->
<li class="user-header"> <li class="user-header">
<img src="<?= Yii::$app->avatar->show(Yii::$app->user->identity->user->username) ?>" class="img-circle" <img src="<?= Yii::$app->avatar->show(Yii::$app->user->identity->user->username) ?>" class="img-circle"
alt="User Image"/> alt="<?= Yii::$app->user->identity->user->username ?>"/>
<p> <p>
<?= Yii::$app->user->identity->user->username ?> - Web Developer <?= Yii::$app->user->identity->user->username ?>
<small>Member since Nov. 2012</small> <small><?= Yii::t('user', 'Registered: {date}', ['date' => date('d.m.Y', Yii::$app->user->identity->user->created_at)]) ?></small>
</p> </p>
</li> </li>
<!-- Menu Body -->
<li class="user-body">
<div class="col-xs-4 text-center">
<a href="#">Followers</a>
</div>
<div class="col-xs-4 text-center">
<a href="#">Sales</a>
</div>
<div class="col-xs-4 text-center">
<a href="#">Friends</a>
</div>
</li>
<!-- Menu Footer--> <!-- Menu Footer-->
<li class="user-footer"> <li class="user-footer">
<div class="pull-left"> <div class="pull-left">
<a href="#" class="btn btn-default btn-flat"><?= Yii::t('main', 'Profile') ?></a> <?= Html::a(
Yii::t('user', 'Profile'),
['/user/profile'],
['class' => 'btn btn-default btn-flat']
) ?>
</div> </div>
<div class="pull-right"> <div class="pull-right">
<?= Html::a( <?= Html::a(
@ -273,11 +265,6 @@ use core\components\avatar_generator\AvatarGenerator;
</li> </li>
</ul> </ul>
</li> </li>
<!-- User Account: style can be found in dropdown.less -->
<li>
<a href="#" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a>
</li>
</ul> </ul>
</div> </div>
</nav> </nav>

2
backend/views/layouts/left.php

@ -46,7 +46,7 @@ $model = new SearchForm();
] ]
], ],
'template' => "{input}", 'template' => "{input}",
])->textInput(['placeholder' => 'Search...'])->label(false)->hint(false); ?> ])->textInput(['placeholder' => Yii::t('main', 'Search...')])->label(false)->hint(false); ?>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
<!-- /.search form --> <!-- /.search form -->

59
backend/views/user/profile.php

@ -0,0 +1,59 @@
<?php
/* @var $this yii\web\View */
/* @var $model \core\forms\user\ProfileEditForm */
/* @var $user \core\entities\user\User */
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
$this->title = Yii::t('user', 'Profile: {username}', ['username' => $user->username]);
$this->params['breadcrumbs'][] = ['label' => $user->username, 'url' => ['view', 'id' => $user->id]];
$this->params['breadcrumbs'][] = Yii::t('user', 'Profile');
?>
<div class="user-profile">
<?php $form = ActiveForm::begin(); ?>
<div class="row">
<div class="col-md-3">
<div class="box box-default">
<div class="box-body">
<div style="text-align: center">
<img style="width: 300px" src="<?= Yii::$app->avatar->show(Yii::$app->user->identity->user->username, null, null, Yii::$app->user->identity->user->user_pic) ?>?<?= rand(10, 5000) ?>" class="img-circle" alt="<?= Yii::$app->user->identity->user->username ?>"/>
</div>
<!-- < ?= $form->field($model, 'user_pic')->fileInput() ?> -->
<?= $form->field($model, 'user_pic')->widget(\kartik\widgets\FileInput::class, [
'options' => [
'accept' => 'image/*'
],
'pluginOptions' => [
'showPreview' => false,
'showCaption' => true,
'showRemove' => true,
'showUpload' => false
],
]); ?>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-default">
<div class="box-body">
<?= $form->field($model, 'username')->textInput(['maxLength' => true]) ?>
<?= $form->field($model, 'email')->textInput(['maxLength' => true]) ?>
<?= $form->field($model, 'password')->passwordInput(['maxLength' => true]) ?>
</div>
</div>
</div>
</div>
<div class="form-group">
<?= Html::submitButton(Yii::t('buttons', 'Save'), ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

2
common/config/main.php

@ -32,6 +32,8 @@ return [
'font_size' => 200, // default: 200 'font_size' => 200, // default: 200
'salt' => 'my_cms_salt', // salt for image file names 'salt' => 'my_cms_salt', // salt for image file names
'texture' => ['sun', 'rain'], // texture name 'texture' => ['sun', 'rain'], // texture name
'texture_over_image' => false,
'text_over_image' => false,
], ],
], ],
]; ];

5
common/modules/blog/entities/queries/BlogPostQuery.php

@ -18,6 +18,11 @@ class BlogPostQuery extends ActiveQuery
]); ]);
} }
public function pubDate()
{
return $this->andWhere(['<', 'published_at', time()]);
}
public function byType($type) public function byType($type)
{ {
return $this->andWhere(['type_id' => $type]); return $this->andWhere(['type_id' => $type]);

6
common/modules/blog/forms/BlogPostForm.php

@ -89,6 +89,12 @@ class BlogPostForm extends CompositeForm
]; ];
} }
public function attributeHints() {
return [
'published_at' => Yii::t('blog', 'The article will be published after the specified date if its status is not a draft'),
];
}
public function categoriesList(): array public function categoriesList(): array
{ {
return ArrayHelper::map(BlogCategory::find()->orderBy('sort')->asArray()->all(), 'id', 'name'); return ArrayHelper::map(BlogCategory::find()->orderBy('sort')->asArray()->all(), 'id', 'name');

2
common/modules/blog/messages/ru/blog.php

@ -61,4 +61,6 @@ return [
'Set tags...' => 'Укажите теги...', 'Set tags...' => 'Укажите теги...',
'Publish' => 'Публикация', 'Publish' => 'Публикация',
'Preview on site' => 'Просмотр на сайте', 'Preview on site' => 'Просмотр на сайте',
'Publish Date' => 'Дата публикации',
'The article will be published after the specified date if its status is not a draft' => 'Статья будет опубликована после наступления указанной даты, если ее статус не черновик.',
]; ];

12
common/modules/blog/repositories/read/BlogPostReadRepository.php

@ -13,34 +13,34 @@ class BlogPostReadRepository
{ {
public function count(): int public function count(): int
{ {
return BlogPost::find()->active()->count(); return BlogPost::find()->active()->pubDate()->count();
} }
public function getAllByRange($offset, $limit): array public function getAllByRange($offset, $limit): array
{ {
return BlogPost::find()->active()->orderBy(['id' => SORT_ASC])->limit($limit)->offset($offset)->all(); return BlogPost::find()->active()->pubDate()->orderBy(['id' => SORT_ASC])->limit($limit)->offset($offset)->all();
} }
public function getAll(): DataProviderInterface public function getAll(): DataProviderInterface
{ {
$query = BlogPost::find()->active()->with('category'); $query = BlogPost::find()->active()->pubDate()->with('category');
return $this->getProvider($query); return $this->getProvider($query);
} }
public function getAllByCategory(BlogCategory $category): DataProviderInterface public function getAllByCategory(BlogCategory $category): DataProviderInterface
{ {
$query = BlogPost::find()->active()->andWhere(['category_id' => $category->id])->with('category'); $query = BlogPost::find()->active()->pubDate()->andWhere(['category_id' => $category->id])->with('category');
return $this->getProvider($query); return $this->getProvider($query);
} }
public function findNext(int $id): ?BlogPost public function findNext(int $id): ?BlogPost
{ {
return BlogPost::find()->active()->andWhere(['>', 'id', $id])->one(); return BlogPost::find()->active()->pubDate()->andWhere(['>', 'id', $id])->one();
} }
public function findPrev(int $id): ?BlogPost public function findPrev(int $id): ?BlogPost
{ {
return BlogPost::find()->active()->andWhere(['<', 'id', $id])->orderBy(['id' => SORT_DESC])->one(); return BlogPost::find()->active()->pubDate()->andWhere(['<', 'id', $id])->orderBy(['id' => SORT_DESC])->one();
} }

42
common/modules/blog/views/manage/post/_form.php

@ -3,7 +3,7 @@
use kartik\file\FileInput; use kartik\file\FileInput;
use mihaildev\ckeditor\CKEditor; use mihaildev\ckeditor\CKEditor;
use yii\helpers\Html; use yii\helpers\Html;
use yii\bootstrap\ActiveForm; use kartik\form\ActiveForm;
use yii\web\JsExpression; use yii\web\JsExpression;
use yii\helpers\Url; use yii\helpers\Url;
use yii\helpers\Json; use yii\helpers\Json;
@ -30,6 +30,22 @@ if (isset($model->_post)) {
JS; JS;
$this->registerJs( $js, $this::POS_READY ); $this->registerJs( $js, $this::POS_READY );
} }
$js2 = '
$(".hint-block").each(function () {
var $hint = $(this);
var label = $hint.parent().find("label");
label.html(label.html() + \' <i style="color:#3c8dbc" class="fa fa-question-circle" aria-hidden="true"></i>\');
label.addClass("help").popover({
html: true,
trigger: "hover",
placement: "bottom",
content: $hint.html()
});
$(this).hide();
});
';
$this->registerJs($js2);
?> ?>
<div class="post-form"> <div class="post-form">
@ -46,24 +62,15 @@ JS;
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="box box-default"> <div class="box box-default">
<div class="box-header with-border"><?= Yii::t('blog', 'Common') ?></div>
<div class="box-body"> <div class="box-body">
<?= $form->field($model, 'category_id')->dropDownList($model->categoriesList(), ['prompt' => '']) ?> <?= $form->field($model, 'category_id')->dropDownList($model->categoriesList(), ['prompt' => '']) ?>
<?= $form->field($model, 'published_at')->widget(DateTimePicker::classname(), [
'options' => [],
'pluginOptions' => [
'autoclose' => true,
'format' => 'dd.mm.yyyy hh:ii:ss',
]
]); ?>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="box box-default"> <div class="box box-default">
<div class="box-header with-border"><?= Yii::t('blog', 'Tags') ?></div>
<div class="box-body"> <div class="box-body">
<?= $form->field($model->tags, 'new_tags')->widget(Select2::classname(), [ <?= $form->field($model->tags, 'new_tags')->widget(Select2::class, [
'options' => [ 'options' => [
'placeholder' => Yii::t('blog','Set tags...'), 'placeholder' => Yii::t('blog','Set tags...'),
'multiple' => true, 'multiple' => true,
@ -81,7 +88,7 @@ JS;
'templateResult' => new JsExpression('function(tag) { return tag.text; }'), 'templateResult' => new JsExpression('function(tag) { return tag.text; }'),
'templateSelection' => new JsExpression('function (tag) { return tag.text; }'), 'templateSelection' => new JsExpression('function (tag) { return tag.text; }'),
], ],
])->label(false); ?> ])->label(Yii::t('blog', 'Tags')); ?>
</div> </div>
</div> </div>
</div> </div>
@ -92,7 +99,7 @@ JS;
<?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?> <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'slug')->textInput(['maxlength' => true]) ?> <?= $form->field($model, 'slug')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'description')->textarea(['rows' => 5]) ?> <?= $form->field($model, 'description')->textarea(['rows' => 5]) ?>
<?= $form->field($model, 'content')->widget(CKEditor::className()) ?> <?= $form->field($model, 'content')->widget(CKEditor::class) ?>
</div> </div>
</div> </div>
@ -154,6 +161,15 @@ JS;
<?= Html::button(Yii::t('blog', 'Preview on site')) ?> <?= Html::button(Yii::t('blog', 'Preview on site')) ?>
<hr> <hr>
<?= $form->field($model, 'status')->radioList(BlogPostHelper::statusList()) ?> <?= $form->field($model, 'status')->radioList(BlogPostHelper::statusList()) ?>
<hr>
<?= $form->field($model, 'published_at')->widget(DateTimePicker::class, [
'options' => [],
'removeButton' => false,
'pluginOptions' => [
'autoclose' => true,
'format' => 'dd.mm.yyyy hh:ii:ss',
]
])->label(Yii::t('blog', 'Publish Date')); ?>
</div> </div>
</div> </div>
</div> </div>

171
core/components/avatar_generator/AvatarGenerator.php

@ -1,171 +0,0 @@
<?php
/**
* Created by Error202
* Date: 18.08.2017
*/
namespace core\components\avatar_generator;
use core\entities\user\User;
use Yii;
use yii\helpers\FileHelper;
use yii\imagine\Image;
use yii\web\NotFoundHttpException;
class AvatarGenerator
{
public $originPath;
public $cachePath;
public function __construct()
{
$this->originPath = Yii::getAlias('@staticRoot') . '/origin';
$this->cachePath = Yii::getAlias('@staticRoot') . '/cache';
}
/**
* @param string $type
* @param string $name
* @param array|null $options
*
* @return string
* @throws NotFoundHttpException
* @throws \yii\base\Exception
*/
public function image(string $type, string $name, array $options = null): string
{
$originPath = $this->originPath . '/' . $type;
$origin = $originPath . '/' . $name;
if (!file_exists($origin))
{
return '';
}
$cachePath = $this->cachePath . '/' . $type;
$cache = $cachePath . '/' . (isset($options['newName']) ? $options['newName'] : $name);
$cacheUrl = Yii::getAlias('@static') . '/cache/' . $type . '/' . $name;
if (isset($options['size']))
{
$size = $options['size'];
}
if (!isset($options['regenerate']))
{
$options['regenerate'] = false;
}
if (!file_exists($origin))
{
throw new NotFoundHttpException('Image "' . $name . '" does not exists.');
}
if (!file_exists($cache) || $options['regenerate'] == true)
{
FileHelper::createDirectory($cachePath, 0755, true);
if (empty($size)) {
//$size = isset(Yii::$app->params['imageSizes'][$type]) ? Yii::$app->params['imageSizes'][$type] : Yii::$app->params['imageSizes']['default'];
$size = [300, 300];
}
Image::thumbnail($origin, $size[0], $size[1])->save($cache);
}
return $cacheUrl;
}
public function avatar(int $id): string
{
if (!$user = User::findOne($id))
{
throw new NotFoundHttpException('User does not exists.');
}
if (empty($user->user_pic) || !file_exists($this->originPath . '/avatar/' . $user->user_pic))
{
$user->user_pic = $this->generateAvatarByName($user->username);
$user->save();
}
return $this->image('avatar', $user->user_pic);
}
public function generateAvatarFromFile($file)
{
$fileName = md5('avatar-' . md5($file) . time()) . '.png';
$originPath = $this->originPath . '/avatar';
$origin = $originPath . '/' . $fileName;
FileHelper::createDirectory($originPath, 0755, true);
copy($file, $origin);
return $fileName;
}
public function generateAvatarByName(string $name): string
{
$width = 300;
$height = 300;
$fontSize = 100;
$font = Yii::getAlias('@core') . '/components/avatar_generator/Play-Bold.ttf';
$parts = explode(' ', $name);
$text = is_array($parts) && count($parts)>1 ? mb_substr($parts[0],0,1,"UTF-8") . mb_substr($parts[1],0,1,"UTF-8") : mb_substr($name,0,1,"UTF-8");
$fileName = md5('avatar-' . $name . time()) . '.png';
$originPath = $this->originPath . '/avatar';
$origin = $originPath . '/' . $fileName;
FileHelper::createDirectory($originPath, 0755, true);
$img = imagecreatetruecolor($width, $height);
$bgcolor = substr(md5($name), 0, 6);
$rgb = [];
list($rgb['r'], $rgb['g'], $rgb['b']) = sscanf($bgcolor, "%02x%02x%02x");
$rgb['rgb'] = $rgb['b'] + ($rgb['g'] << 0x8) + ($rgb['r'] << 0x10);
$contrast = $this->RgbContrast($rgb['r'], $rgb['g'], $rgb['b']);
$fillColor = imagecolorallocate($img, $rgb['r'], $rgb['g'], $rgb['b']);
imagefill($img, 0,0, $fillColor);
$cor = imagecolorallocate($img, $contrast['r'], $contrast['g'], $contrast['b']);
$box = imageftbbox( $fontSize, 0, $font, $text );
$x = ($width - ($box[2] - $box[0])) / 2;
$y = ($height - ($box[1] - $box[7])) / 2;
$y -= $box[7];
imagettftext($img, $fontSize, 0, $x, $y, $cor, $font, $text);
imagepng($img, $origin);
imagedestroy($img);
return $fileName;
}
public function getPath(string $type): string
{
if (!file_exists($this->originPath . '/' . $type))
{
mkdir($this->originPath . '/' . $type, 0777, true);
}
return $this->originPath . '/' . $type;
}
public function remove(string $type, string $fileName = null): void
{
if ($fileName && file_exists($this->originPath . '/' . $type . '/' . $fileName)) {
unlink($this->originPath . '/' . $type . '/' . $fileName);
}
if ($fileName && file_exists($this->cachePath . '/' . $type . '/' . $fileName)) {
unlink($this->cachePath . '/' . $type . '/' . $fileName);
}
}
public function RgbContrast($r, $g, $b) {
return array(
'r' => ($r < 128) ? 255 : 0,
'g' => ($g < 128) ? 255 : 0,
'b' => ($b < 128) ? 255 : 0
);
}
}

BIN
core/components/avatar_generator/Play-Bold.ttf

Binary file not shown.

13
core/entities/user/User.php

@ -1,7 +1,6 @@
<?php <?php
namespace core\entities\user; namespace core\entities\user;
use core\components\avatar_generator\AvatarGenerator;
use core\entities\Session; use core\entities\Session;
use core\events\EventTrait; use core\events\EventTrait;
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior; use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;
@ -12,6 +11,7 @@ use Yii;
use yii\behaviors\TimestampBehavior; use yii\behaviors\TimestampBehavior;
use yii\db\ActiveQuery; use yii\db\ActiveQuery;
use yii\db\ActiveRecord; use yii\db\ActiveRecord;
use zertex\avatar_generator\AvatarGenerator;
/** /**
* User model * User model
@ -91,13 +91,16 @@ class User extends ActiveRecord implements AggregateRoot
$this->setPassword( $password ); $this->setPassword( $password );
} }
$this->updated_at = time(); $this->updated_at = time();
/* @var $user_pic \yii\web\UploadedFile */
if ($user_pic) if ($user_pic)
{ {
$fileName = md5('avatar-' . $user_pic->baseName . time()) . '.' . $user_pic->extension; $fileName = md5('avatar-' . $user_pic->baseName . time()) . '.' . $user_pic->extension;
if ($user_pic->saveAs((new AvatarGenerator())->getPath('avatar') . '/' . $fileName)) $path = Yii::getAlias( '@runtime/' . $fileName);
{ $user_pic->saveAs($path);
(new AvatarGenerator())->remove('avatar', $this->user_pic); $this->user_pic = basename(Yii::$app->avatar->update($username, null, $path));
$this->user_pic = $fileName; if (file_exists($path)) {
unlink($path);
} }
} }
} }

8
core/forms/user/ProfileEditForm.php

@ -16,6 +16,7 @@ class ProfileEditForm extends Model
public $email; public $email;
public $username; public $username;
public $password; public $password;
public $user_pic;
public $_user; public $_user;
@ -24,6 +25,7 @@ class ProfileEditForm extends Model
$this->email = $user->email; $this->email = $user->email;
$this->username = $user->username; $this->username = $user->username;
$this->_user = $user; $this->_user = $user;
$this->user_pic = $user->user_pic;
parent::__construct($config); parent::__construct($config);
} }
@ -36,6 +38,11 @@ class ProfileEditForm extends Model
[['email', 'password'], 'string', 'max' => 255], [['email', 'password'], 'string', 'max' => 255],
[['username', 'email'], 'unique', 'targetClass' => User::class, 'filter' => ['<>', 'id', $this->_user->id]], [['username', 'email'], 'unique', 'targetClass' => User::class, 'filter' => ['<>', 'id', $this->_user->id]],
['username', 'string', 'min' => 2, 'max' => 255], ['username', 'string', 'min' => 2, 'max' => 255],
['user_pic', 'image', 'extensions' => 'png, jpg, jpeg, gif',
'skipOnEmpty' => true,
'minWidth' => 100,
'minHeight' => 100,
],
]; ];
} }
@ -45,6 +52,7 @@ class ProfileEditForm extends Model
'email' => Yii::t('user', 'E-mail'), 'email' => Yii::t('user', 'E-mail'),
'username' => Yii::t('user', 'Username'), 'username' => Yii::t('user', 'Username'),
'password' => Yii::t('user', 'Password'), 'password' => Yii::t('user', 'Password'),
'user_pic' => Yii::t('user', 'User Picture'),
]; ];
} }
} }

2
core/services/user/ProfileService.php

@ -21,7 +21,7 @@ class ProfileService
public function edit($id, ProfileEditForm $form): void public function edit($id, ProfileEditForm $form): void
{ {
$user = $this->users->get($id); $user = $this->users->get($id);
$user->editProfile($form->email, $form->username, $form->password); $user->editProfile($form->email, $form->username, $form->password, $form->user_pic);
$this->users->save($user); $this->users->save($user);
} }
} }

81
docs/develope/Modules.md

@ -0,0 +1,81 @@
### Модули
Модули располагаются в папке `common/modules`
Папка модуля должна содержать основной файл, например, `BlogModule.php` следующего содержания
```php
<?php
namespace common\modules\blog;
use core\components\modules\ModuleInterface;
use yii\helpers\ArrayHelper;
class BlogModule extends \yii\base\Module implements ModuleInterface
{
// Папка контроллеров модуля
public $controllerNamespace = 'common\modules\blog\controllers';
// Инициализация модуля
public function init()
{
parent::init();
// custom initialization code goes here
}
// Автозапуск модуля
public function bootstrap($app)
{
// Добавление правила поиска по данному модулю
$app->params['search_rules'][] = "SELECT title, content, CONCAT('/blog/manage/post/view/', id) AS url FROM {{blog_posts}}";
// Добавление правила роутинга
$app->getUrlManager()->addRules([
'blog' => 'blog/post/index',
]);
// Добавление правил роутинга, в случае использования классов
$app->getUrlManager()->addRules([
['class' => 'common\modules\blog\urls\BlogPostUrlRule'],
['class' => 'common\modules\blog\urls\BlogCategoryUrlRule'],
]);
// Добавление локализации
$app->getI18n()->translations = ArrayHelper::merge($app->getI18n()->translations, [
'blog' => [
'class' => 'yii\i18n\PhpMessageSource',
'basePath' => '@common/modules/blog/messages',
],
'blog_public' => [
'class' => 'yii\i18n\PhpMessageSource',
'basePath' => '@common/modules/blog/messages',
],
]);
// Добавление пунктов в меню администратора
if (basename($app->getBasePath()) === 'backend') {
$app->params['adminMenu'][] = [
'label' => \Yii::t( 'blog', 'Blog' ),
'icon' => 'book',
'items' => [
[
'label' => \Yii::t( 'blog', 'Categories' ),
'icon' => 'caret-right',
'url' => [ '/blog/manage/category/index' ]
],
[
'label' => \Yii::t( 'blog', 'Posts' ),
'icon' => 'caret-right',
'url' => [ '/blog/manage/post/index' ]
],
[
'label' => \Yii::t( 'blog', 'Comments' ),
'icon' => 'caret-right',
'url' => [ '/blog/manage/comment/index' ]
],
],
'visible' => \Yii::$app->user->can( 'admin' ) || \Yii::$app->user->can( 'BlogManagement' )
];
}
}
}
```

18
docs/develope/README.md

@ -0,0 +1,18 @@
# Zertex CMS
Система управления сайтом на Yii2
**Для разработчика**
* [Модули](Modules.md)
#### Дополнительные параметры Yii::$app
1. `$app->params['search_rules']` - Массив правил для поиска.
Пример:
```php
$app->params['search_rules'][] = "SELECT title, content, CONCAT('/blog/manage/post/view/', id) AS url FROM {{blog_posts}}";
```
Поиск осуществляется по полям TITLE и CONTENT. В качестве URL указывается ссылка на конкрутную запись.
Если название полей отличаются от TITLE и CONTENT, но, сгласно правилам SQL, указывайте ваше поле, затем `as TITLE` или `as CONTENT`
Loading…
Cancel
Save