Browse Source

Global rewrite

master
Egorka 7 years ago
parent
commit
b98790153c
  1. 2
      .editorconfig
  2. 9
      README.md
  3. 16
      backend/bootstrap/SetUp.php
  4. 113
      backend/components/ToggleAction.php
  5. 143
      backend/components/ToggleColumn.php
  6. 3
      backend/config/urlManager.php
  7. 150
      backend/controllers/SettingsController.php
  8. 66
      backend/forms/SettingsSearch.php
  9. 2
      backend/messages/ru/buttons.php
  10. 16
      backend/messages/ru/main.php
  11. 9
      backend/views/layouts/header.php
  12. 10
      backend/views/layouts/left.php
  13. 2
      backend/views/page/_form.php
  14. 43
      backend/views/settings/_form.php
  15. 24
      backend/views/settings/create.php
  16. 70
      backend/views/settings/index.php
  17. 29
      backend/views/settings/update.php
  18. 60
      backend/views/settings/view.php
  19. 25
      common/bootstrap/SetUp.php
  20. 7
      common/config/params.php
  21. 93
      common/modules/blog/BlogModule.php
  22. 162
      common/modules/blog/controllers/PostController.php
  23. 155
      common/modules/blog/controllers/manage/CategoryController.php
  24. 159
      common/modules/blog/controllers/manage/CommentController.php
  25. 202
      common/modules/blog/controllers/manage/PostController.php
  26. 151
      common/modules/blog/controllers/manage/TagController.php
  27. 88
      common/modules/blog/entities/BlogCategory.php
  28. 93
      common/modules/blog/entities/BlogComment.php
  29. 347
      common/modules/blog/entities/BlogPost.php
  30. 46
      common/modules/blog/entities/BlogTag.php
  31. 29
      common/modules/blog/entities/BlogTagAssignment.php
  32. 35
      common/modules/blog/entities/queries/BlogPostQuery.php
  33. 71
      common/modules/blog/forms/BlogCategoryForm.php
  34. 37
      common/modules/blog/forms/BlogCommentEditForm.php
  35. 20
      common/modules/blog/forms/BlogCommentForm.php
  36. 108
      common/modules/blog/forms/BlogPostForm.php
  37. 46
      common/modules/blog/forms/BlogTagForm.php
  38. 45
      common/modules/blog/forms/BlogTagSingleForm.php
  39. 57
      common/modules/blog/forms/search/BlogCategorySearch.php
  40. 72
      common/modules/blog/forms/search/BlogCommentSearch.php
  41. 69
      common/modules/blog/forms/search/BlogPostSearch.php
  42. 55
      common/modules/blog/forms/search/BlogTagSearch.php
  43. 70
      common/modules/blog/helpers/BlogPostHelper.php
  44. 61
      common/modules/blog/messages/ru/blog.php
  45. 8
      common/modules/blog/messages/ru/blog_public.php
  46. 31
      common/modules/blog/migrations/m180605_184534_create_blog_categories_table.php
  47. 42
      common/modules/blog/migrations/m180605_190347_create_blog_posts_table.php
  48. 28
      common/modules/blog/migrations/m180605_193423_create_blog_tags_table.php
  49. 36
      common/modules/blog/migrations/m180605_193748_create_blog_tag_assignments_table.php
  50. 43
      common/modules/blog/migrations/m180605_194207_create_blog_comments_table.php
  51. 31
      common/modules/blog/repositories/BlogCategoryRepository.php
  52. 36
      common/modules/blog/repositories/BlogRepository.php
  53. 36
      common/modules/blog/repositories/BlogTagRepository.php
  54. 23
      common/modules/blog/repositories/read/BlogCategoryReadRepository.php
  55. 94
      common/modules/blog/repositories/read/BlogPostReadRepository.php
  56. 18
      common/modules/blog/repositories/read/BlogTagReadRepository.php
  57. 66
      common/modules/blog/services/BlogCategoryManageService.php
  58. 37
      common/modules/blog/services/BlogCommentManageService.php
  59. 32
      common/modules/blog/services/BlogCommentService.php
  60. 162
      common/modules/blog/services/BlogPostManageService.php
  61. 43
      common/modules/blog/services/BlogTagManageService.php
  62. 91
      common/modules/blog/urls/BlogCategoryUrlRule.php
  63. 88
      common/modules/blog/urls/BlogMainUrlRule.php
  64. 91
      common/modules/blog/urls/BlogTagUrlRule.php
  65. 54
      common/modules/blog/views/manage/category/_form.php
  66. 17
      common/modules/blog/views/manage/category/create.php
  67. 54
      common/modules/blog/views/manage/category/index.php
  68. 19
      common/modules/blog/views/manage/category/update.php
  69. 70
      common/modules/blog/views/manage/category/view.php
  70. 54
      common/modules/blog/views/manage/comment/index.php
  71. 34
      common/modules/blog/views/manage/comment/update.php
  72. 71
      common/modules/blog/views/manage/comment/view.php
  73. 144
      common/modules/blog/views/manage/post/_form.php
  74. 17
      common/modules/blog/views/manage/post/create.php
  75. 76
      common/modules/blog/views/manage/post/index.php
  76. 21
      common/modules/blog/views/manage/post/update.php
  77. 138
      common/modules/blog/views/manage/post/view.php
  78. 29
      common/modules/blog/views/manage/tag/_form.php
  79. 17
      common/modules/blog/views/manage/tag/create.php
  80. 47
      common/modules/blog/views/manage/tag/index.php
  81. 19
      common/modules/blog/views/manage/tag/update.php
  82. 39
      common/modules/blog/views/manage/tag/view.php
  83. 42
      common/modules/blog/views/post/_list.php
  84. 51
      common/modules/blog/views/post/_post.php
  85. 40
      common/modules/blog/views/post/category.php
  86. 13
      common/modules/blog/views/post/index.php
  87. 114
      common/modules/blog/views/post/post.php
  88. 29
      common/modules/blog/widgets/CategoriesWidget.php
  89. 20
      common/modules/blog/widgets/CommentView.php
  90. 57
      common/modules/blog/widgets/CommentsWidget.php
  91. 39
      common/modules/blog/widgets/TagWidget.php
  92. 32
      common/modules/blog/widgets/views/categories/categories.php
  93. 82
      common/modules/blog/widgets/views/comments/_comment.php
  94. 103
      common/modules/blog/widgets/views/comments/comments.php
  95. 21
      common/modules/blog/widgets/views/tags/tags.php
  96. 2
      common/runtime/.gitignore
  97. 1
      common/runtime/cache/00/003d3baedc022e4d084a532960a55cc3.bin
  98. 1
      common/runtime/cache/00/00431a2d632e2abcfe7139037442a6ef.bin
  99. 1
      common/runtime/cache/00/0045808e17fdda07b6043f2858e01289.bin
  100. 1
      common/runtime/cache/00/005ab8900f97feeb9ab462ab8b45a4b2.bin
  101. Some files were not shown because too many files have changed in this diff Show More

2
.editorconfig

@ -45,7 +45,7 @@ indent_size=2
indent_style=space indent_style=space
indent_size=2 indent_size=2
[{*.yml,*.yaml}] [{*.yml,*.yaml,*.xml}]
indent_style=space indent_style=space
indent_size=2 indent_size=2

9
README.md

@ -1,8 +1,5 @@
<p align="center"> <p align="center">
<a href="https://github.com/yiisoft" target="_blank"> <h1 align="center">ZxCMS - Yii2 site management system</h1>
<img src="https://avatars0.githubusercontent.com/u/993323" height="100px">
</a>
<h1 align="center">Yii 2 Advanced Project Template</h1>
<br> <br>
</p> </p>
@ -17,10 +14,6 @@ deploying the application in different environments.
Documentation is at [docs/guide/README.md](docs/guide/README.md). Documentation is at [docs/guide/README.md](docs/guide/README.md).
[![Latest Stable Version](https://poser.pugx.org/yiisoft/yii2-app-advanced/v/stable.png)](https://packagist.org/packages/yiisoft/yii2-app-advanced)
[![Total Downloads](https://poser.pugx.org/yiisoft/yii2-app-advanced/downloads.png)](https://packagist.org/packages/yiisoft/yii2-app-advanced)
[![Build Status](https://travis-ci.org/yiisoft/yii2-app-advanced.svg?branch=master)](https://travis-ci.org/yiisoft/yii2-app-advanced)
DIRECTORY STRUCTURE DIRECTORY STRUCTURE
------------------- -------------------

16
backend/bootstrap/SetUp.php

@ -2,9 +2,11 @@
namespace backend\bootstrap; namespace backend\bootstrap;
use core\entities\Settings;
use mihaildev\ckeditor\CKEditor; use mihaildev\ckeditor\CKEditor;
use mihaildev\elfinder\ElFinder; use mihaildev\elfinder\ElFinder;
use yii\base\BootstrapInterface; use yii\base\BootstrapInterface;
use yii\helpers\ArrayHelper;
class SetUp implements BootstrapInterface class SetUp implements BootstrapInterface
{ {
@ -14,5 +16,19 @@ class SetUp implements BootstrapInterface
$container->set(CKEditor::class, [ $container->set(CKEditor::class, [
'editorOptions' => ElFinder::ckeditorOptions('elfinder', []), 'editorOptions' => ElFinder::ckeditorOptions('elfinder', []),
]); ]);
// load settings
$settings = ArrayHelper::map(Settings::find()->andWhere(['active' => 1])->all(), 'key', 'value', 'section');
$app->params['settings'] = $settings;
// Connect backend modules
// Add finish UrlRules
$app->getUrlManager()->addRules([
'<_c:[\w\-]+>' => '<_c>/index',
'<_c:[\w\-]+>/<id:\d+>' => '<_c>/view',
'<_c:[\w\-]+>/<_a:[\w-]+>' => '<_c>/<_a>',
'<_c:[\w\-]+>/<id:\d+>/<_a:[\w\-]+>' => '<_c>/<_a>',
]);
} }
} }

113
backend/components/ToggleAction.php

@ -0,0 +1,113 @@
<?php
/**
* Created by Error202
* Date: 04.06.2018
*/
namespace backend\components;
use yii\base\Action;
use yii\db\Expression;
use yii\base\InvalidConfigException;
use yii\web\MethodNotAllowedHttpException;
use Yii;
class ToggleAction extends Action
{
/**
* @var string name of the model
*/
public $modelClass;
/**
* @var string model attribute
*/
public $attribute = 'active';
/**
* @var string scenario model
*/
public $scenario = null;
/**
* @var string|array additional condition for loading the model
*/
public $andWhere;
/**
* @var string|int|boolean|Expression what to set active models to
*/
public $onValue = 1;
/**
* @var string|int|boolean what to set inactive models to
*/
public $offValue = 0;
/**
* @var bool whether to set flash messages or not
*/
public $setFlash = false;
/**
* @var string flash message on success
*/
public $flashSuccess = "Model saved";
/**
* @var string flash message on error
*/
public $flashError = "Error saving Model";
/**
* @var string|array URL to redirect to
*/
public $redirect;
/**
* @var string pk field name
*/
public $primaryKey = 'id';
public function run($id)
{
if (!Yii::$app->request->getIsPost()) {
throw new MethodNotAllowedHttpException();
}
$id = (int)$id;
$result = null;
if (empty($this->modelClass) || !class_exists($this->modelClass)) {
throw new InvalidConfigException("Model class doesn't exist");
}
/* @var $modelClass \yii\db\ActiveRecord */
$modelClass = $this->modelClass;
$attribute = $this->attribute;
$model = $modelClass::find()->where([$this->primaryKey => $id]);
if (!empty($this->andWhere)) {
$model->andWhere($this->andWhere);
}
$model = $model->one();
if (!is_null($this->scenario)) {
$model->scenario = $this->scenario;
}
if (!$model->hasAttribute($this->attribute)) {
throw new InvalidConfigException("Attribute doesn't exist");
}
if ($model->$attribute == $this->onValue) {
$model->$attribute = $this->offValue;
} elseif ($this->onValue instanceof Expression && $model->$attribute != $this->offValue) {
$model->$attribute = $this->offValue;
} else {
$model->$attribute = $this->onValue;
}
if ($model->save()) {
if ($this->setFlash) {
Yii::$app->session->setFlash('success', $this->flashSuccess);
}
} else {
if ($this->setFlash) {
Yii::$app->session->setFlash('error', $this->flashError);
}
}
if (Yii::$app->request->getIsAjax()) {
Yii::$app->end();
}
/* @var $controller \yii\web\Controller */
$controller = $this->controller;
if (!empty($this->redirect)) {
return $controller->redirect($this->redirect);
}
return $controller->redirect(Yii::$app->request->getReferrer());
}
}

143
backend/components/ToggleColumn.php

@ -0,0 +1,143 @@
<?php
/**
* Created by Error202
* Date: 04.06.2018
*/
namespace backend\components;
use yii\grid\DataColumn;
use yii\helpers\Html;
use yii\web\View;
use Yii;
class ToggleColumn extends DataColumn
{
/**
* Toggle action that will be used as the toggle action in your controller
* @var string
*/
public $action = 'toggle';
/**
* @var string pk field name
*/
public $primaryKey = 'primaryKey';
/**
* Whether to use ajax or not
* @var bool
*/
public $enableAjax = true;
/**
* @var string glyphicon for 'on' value
*/
public $iconOn = 'ok';
/**
* @var string glyphicon for 'off' value
*/
public $iconOff = 'remove';
/**
* @var string text to display on the 'on' link
*/
public $onText;
/**
* @var string text to display on the 'off' link
*/
public $offText;
/**
* @var string text to display next to the 'on' link
*/
public $displayValueText = false;
/**
* @var string text to display next to the 'on' link
*/
public $onValueText;
/**
* @var string text to display next to the 'off' link
*/
public $offValueText;
public function init()
{
if ($this->onText === null) {
$this->onText = Yii::t('main', 'On');
}
if ($this->offText === null) {
$this->offText = Yii::t('main', 'Off');
}
if ($this->onValueText === null) {
$this->onValueText = Yii::t('main', 'Active');
}
if ($this->offValueText === null) {
$this->offValueText = Yii::t('main', 'Inactive');
}
if ($this->enableAjax) {
$this->registerJs();
}
}
/**
* @inheritdoc
*/
protected function renderDataCellContent($model, $key, $index)
{
$url = [$this->action, 'id' => $model->{$this->primaryKey}];
$attribute = $this->attribute;
$value = $model->$attribute;
if ($value === null || $value == true) {
$icon = $this->iconOn;
$title = $this->offText;
$valueText = $this->onValueText;
$color = 'green';
} else {
$icon = $this->iconOff;
$title = $this->onText;
$valueText = $this->offValueText;
$color = 'red';
}
return Html::a(
'<span class="glyphicon glyphicon-' . $icon . '"></span>',
$url,
[
'title' => $title,
'class' => 'toggle-column',
'style' => 'color:' . $color,
'data-method' => 'post',
'data-pjax' => '0',
]
) . ( $this->displayValueText ? " {$valueText}" : "" );
}
/**
* Registers the ajax JS
*/
public function registerJs()
{
if(Yii::$app->request->isAjax) {
return;
}
$js = <<<'JS'
$(document.body).on("click", "a.toggle-column", function(e) {
e.preventDefault();
$.post($(this).attr("href"), function(data) {
var pjaxId = $(e.target).closest("[data-pjax-container]").attr("id");
$.pjax.reload({container:"#" + pjaxId});
});
return false;
});
JS;
$this->grid->view->registerJs($js, View::POS_READY, 'zx-toggle-column');
}
}

3
backend/config/urlManager.php

@ -12,9 +12,10 @@ return [
'' => 'site/index', '' => 'site/index',
'<_a:login|logout>' => 'auth/<_a>', '<_a:login|logout>' => 'auth/<_a>',
/* Moved to end of bootstrap - SetUp.php
'<_c:[\w\-]+>' => '<_c>/index', '<_c:[\w\-]+>' => '<_c>/index',
'<_c:[\w\-]+>/<id:\d+>' => '<_c>/view', '<_c:[\w\-]+>/<id:\d+>' => '<_c>/view',
'<_c:[\w\-]+>/<_a:[\w-]+>' => '<_c>/<_a>', '<_c:[\w\-]+>/<_a:[\w-]+>' => '<_c>/<_a>',
'<_c:[\w\-]+>/<id:\d+>/<_a:[\w\-]+>' => '<_c>/<_a>', '<_c:[\w\-]+>/<id:\d+>/<_a:[\w\-]+>' => '<_c>/<_a>',*/
], ],
]; ];

150
backend/controllers/SettingsController.php

@ -0,0 +1,150 @@
<?php
/**
* Created by Error202
* Date: 04.06.2018
*/
namespace backend\controllers;
use backend\forms\SettingsSearch;
use core\entities\Settings;
use core\forms\SettingsForm;
use core\services\SettingsService;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\web\Controller;
use backend\components\ToggleAction;
use Yii;
use yii\web\NotFoundHttpException;
class SettingsController extends Controller
{
private $service;
public function __construct( string $id, $module, SettingsService $service, array $config = [] ) {
parent::__construct( $id, $module, $config );
$this->service = $service;
}
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['create','view','index', 'update', 'delete', 'toggle'],
'allow' => true,
'roles' => ['SettingsManagement'],
],
[ // all the action are accessible to admin
'allow' => true,
'roles' => ['admin'],
],
],
],
];
}
public function actions()
{
return [
'toggle' => [
'class' => ToggleAction::className(),
'modelClass' => Settings::class,
//'setFlash' => true,
]
];
}
public function actionIndex()
{
$searchModel = new SettingsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render(
'index',
[
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]
);
}
public function actionView($id)
{
return $this->render(
'view',
[
'model' => $this->findModel($id),
]
);
}
public function actionCreate()
{
$form = new SettingsForm();
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$settings = $this->service->create($form);
return $this->redirect(['view', 'id' => $settings->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
else {
$form->active = 1;
}
return $this->render(
'create',
[
'model' => $form,
]
);
}
public function actionUpdate($id)
{
$settings = $this->findModel($id);
$form = new SettingsForm($settings);
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$this->service->edit($settings->id, $form);
return $this->redirect(['view', 'id' => $settings->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render(
'update',
[
'model' => $form,
'settings' => $settings,
]
);
}
public function actionDelete($id)
{
$this->service->remove($id);
return $this->redirect(['index']);
}
protected function findModel($id)
{
if (($model = Settings::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}

66
backend/forms/SettingsSearch.php

@ -0,0 +1,66 @@
<?php
/**
* Created by Error202
* Date: 04.06.2018
*/
namespace backend\forms;
use core\entities\Settings;
use yii\base\Model;
use yii\data\ActiveDataProvider;
class SettingsSearch extends Settings
{
public $id;
public $type;
public $section;
public $key;
public $value;
public $active;
public function rules()
{
return [
[['id'], 'integer'],
[['active'], 'boolean'],
[['type', 'section', 'key', 'value'], 'safe'],
];
}
/**
* @return array
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* @param $params
* @return ActiveDataProvider
*/
public function search($params)
{
$query = Settings::find();
$dataProvider = new ActiveDataProvider(
[
'query' => $query,
]
);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->andFilterWhere(
[
'id' => $this->id,
'active' => $this->active,
'section' => $this->section,
]
);
$query->andFilterWhere(['like', 'key', $this->key])
->andFilterWhere(['like', 'value', $this->value]);
return $dataProvider;
}
}

2
backend/messages/ru/buttons.php

@ -7,4 +7,6 @@ return [
'Delete' => 'Удалить', 'Delete' => 'Удалить',
'Edit' => 'Изменить', 'Edit' => 'Изменить',
'Editing' => 'Редактирование', 'Editing' => 'Редактирование',
'All Settings' => 'Все настройки',
'Create Setting' => 'Новый параметр',
]; ];

16
backend/messages/ru/main.php

@ -8,4 +8,20 @@ return [
'Profile' => 'Профиль', 'Profile' => 'Профиль',
'Error' => 'Ошибка', 'Error' => 'Ошибка',
'Return to back or login page please.' => 'Вернитесь назад или авторизуйтесь снова.', 'Return to back or login page please.' => 'Вернитесь назад или авторизуйтесь снова.',
'{attribute} "{value}" already exists for this section.' => '{attribute} "{value}" уже есть в этом разделе.',
'"{attribute}" must be a valid JSON object' => '"{attribute}" должен быть в формате Json',
'Please select correct type' => 'Укажите правильный тип',
'Settings' => 'Настройки',
'Type' => 'Тип',
'Section' => 'Раздел',
'Key' => 'Ключ',
'Value' => 'Значение',
'Active' => 'Активная',
'Created At' => 'Создано',
'Updated At' => 'Обновлено',
'On' => 'Включить',
'Off' => 'Выключить',
'Updating Setting' => 'Редактирование параметра',
'Editing' => 'Редактирование',
'Change at your own risk' => 'Редактируйте на свой страх и риск',
]; ];

9
backend/views/layouts/header.php

@ -1,13 +1,16 @@
<?php <?php
use yii\helpers\Html; use yii\helpers\Html;
use core\components\avatar_generator\AvatarGenerator;
/* @var $this \yii\web\View */ /* @var $this \yii\web\View */
/* @var $content string */ /* @var $content string */
$avatar = new AvatarGenerator();
?> ?>
<header class="main-header"> <header class="main-header">
<?= Html::a('<span class="logo-mini">APP</span><span class="logo-lg">' . Yii::$app->name . '</span>', Yii::$app->homeUrl, ['class' => 'logo']) ?> <?= Html::a('<span class="logo-mini">'.(isset(Yii::$app->params['settings']['site']['short_name']) ? Yii::$app->params['settings']['site']['short_name'] : 'APP').'</span><span class="logo-lg">' . (isset(Yii::$app->params['settings']['site']['name']) ? Yii::$app->params['settings']['site']['name'] : Yii::$app->name) . '</span>', Yii::$app->homeUrl, ['class' => 'logo']) ?>
<nav class="navbar navbar-static-top" role="navigation"> <nav class="navbar navbar-static-top" role="navigation">
@ -229,13 +232,13 @@ use yii\helpers\Html;
<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="<?= $directoryAsset ?>/img/user2-160x160.jpg" class="user-image" alt="User Image"/> <img src="<?= $avatar->avatar(Yii::$app->user->id) ?>" class="user-image" alt="User Image"/>
<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="<?= $directoryAsset ?>/img/user2-160x160.jpg" class="img-circle" <img src="<?= $avatar->avatar(Yii::$app->user->id) ?>" class="img-circle"
alt="User Image"/> alt="User Image"/>
<p> <p>

10
backend/views/layouts/left.php

@ -29,7 +29,7 @@
<?= dmstr\widgets\Menu::widget( <?= dmstr\widgets\Menu::widget(
[ [
'options' => ['class' => 'sidebar-menu tree', 'data-widget'=> 'tree'], 'options' => ['class' => 'sidebar-menu tree', 'data-widget'=> 'tree'],
'items' => [ 'items' => \yii\helpers\ArrayHelper::merge([
['label' => Yii::t('main', 'Menu'), 'options' => ['class' => 'header']], ['label' => Yii::t('main', 'Menu'), 'options' => ['class' => 'header']],
[ [
@ -77,7 +77,13 @@
'visible' => \Yii::$app->user->can('admin') || \Yii::$app->user->can('SliderManagement'), 'visible' => \Yii::$app->user->can('admin') || \Yii::$app->user->can('SliderManagement'),
], ],
], [
'label' => Yii::t('main', 'Settings'),
'icon' => 'cog', 'url' => ['/settings/index'],
'active' => $this->context->id == 'settings',
'visible' => \Yii::$app->user->can('admin') || \Yii::$app->user->can('SettingsManagement'),
],
], Yii::$app->params['adminMenu']),
] ]
) ?> ) ?>

2
backend/views/page/_form.php

@ -34,7 +34,7 @@ use yii\widgets\ActiveForm;
</div> </div>
<div class="form-group"> <div class="form-group">
<?= Html::submitButton(Yii::t('page', Yii::t('buttons', 'Save')), ['class' => 'btn btn-success']) ?> <?= Html::submitButton(Yii::t('buttons', 'Save'), ['class' => 'btn btn-success']) ?>
</div> </div>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>

43
backend/views/settings/_form.php

@ -0,0 +1,43 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use core\forms\SettingsForm;
/**
* @var yii\web\View $this
* @var SettingsForm $model
* @var yii\widgets\ActiveForm $form
*/
?>
<div class="setting-form">
<div class="box box-default">
<div class="box-body">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'section')->textInput(['maxlength' => 255]) ?>
<?= $form->field($model, 'key')->textInput(['maxlength' => 255]) ?>
<?= $form->field($model, 'value')->textarea(['rows' => 6]) ?>
<?= $form->field($model, 'active')->checkbox(['value' => 1]) ?>
<?=
$form->field($model, 'type')->dropDownList(
$model->getTypes()
)->hint(Yii::t('main', 'Change at your own risk')) ?>
<div class="form-group">
<?= Html::submitButton(Yii::t('buttons', 'Save'), ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>

24
backend/views/settings/create.php

@ -0,0 +1,24 @@
<?php
use core\forms\SettingsForm;
/**
* @var yii\web\View $this
* @var SettingsForm $model
*/
$this->title = Yii::t('main','Create Setting');
$this->params['breadcrumbs'][] = ['label' => Yii::t('main', 'Settings'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="setting-create">
<?=
$this->render(
'_form',
[
'model' => $model,
]
) ?>
</div>

70
backend/views/settings/index.php

@ -0,0 +1,70 @@
<?php
use yii\helpers\Html;
use yii\grid\GridView;
use yii\helpers\ArrayHelper;
use yii\widgets\Pjax;
use core\entities\Settings;
use backend\components\ToggleColumn;
/**
* @var yii\web\View $this
* @var \backend\forms\SettingsSearch $searchModel
* @var yii\data\ActiveDataProvider $dataProvider
*/
$this->title = Yii::t('main', 'Settings');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="setting-index">
<p>
<?=
Html::a(
Yii::t('buttons','Create Setting'),
['create'],
['class' => 'btn btn-success']
) ?>
</p>
<div class="box">
<div class="box-body">
<?php Pjax::begin(); ?>
<?=
GridView::widget(
[
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
//'id',
//'type',
[
'attribute' => 'section',
'filter' => ArrayHelper::map(
Settings::find()->select('section')->distinct()->where(['<>', 'section', ''])->all(),
'section',
'section'
),
],
'key',
'value:ntext',
[
'class' => ToggleColumn::class,
'attribute' => 'active',
'filter' => [1 => Yii::t('yii', 'Yes'), 0 => Yii::t('yii', 'No')],
'options' => ['style' => 'width: 100px;'],
'contentOptions' => ['class' => 'text-center'],
],
[
'class' => 'yii\grid\ActionColumn',
'options' => ['style' => 'width: 100px;'],
'contentOptions' => ['class' => 'text-center'],
],
],
]
); ?>
<?php Pjax::end(); ?>
</div>
</div>
</div>

29
backend/views/settings/update.php

@ -0,0 +1,29 @@
<?php
use core\forms\SettingsForm;
use core\entities\Settings;
/**
* @var yii\web\View $this
* @var SettingsForm $model
* @var Settings $settings
*/
$this->title = Yii::t(
'main',
'Updating Setting') . ' ' . $model->section. '.' . $model->key;
$this->params['breadcrumbs'][] = ['label' => Yii::t('main', 'Settings'), 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->section. '.' . $model->key, 'url' => ['view', 'id' => $settings->id]];
$this->params['breadcrumbs'][] = Yii::t('main', 'Editing');
?>
<div class="setting-update">
<?=
$this->render(
'_form',
[
'model' => $model,
]
) ?>
</div>

60
backend/views/settings/view.php

@ -0,0 +1,60 @@
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
/**
* @var yii\web\View $this
* @var \core\entities\Settings $model
*/
$this->title = $model->section. '.' . $model->key;
$this->params['breadcrumbs'][] = ['label' => Yii::t('main', 'Settings'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="setting-view">
<p>
<?= Html::a(Yii::t('buttons','All Settings'), ['index'], ['class' => 'btn btn-default']) ?>
<?= Html::a(Yii::t('buttons', 'Edit'), ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
<?=
Html::a(
Yii::t('buttons', 'Delete'),
['delete', 'id' => $model->id],
[
'class' => 'btn btn-danger',
'data' => [
'confirm' => Yii::t('buttons', 'Are you sure you want to delete this item?'),
'method' => 'post',
],
]
) ?>
</p>
<div class="box">
<div class="box-body">
<?=
DetailView::widget(
[
'model' => $model,
'attributes' => [
'id',
'type',
'section',
'active:boolean',
'key',
'value:ntext',
[
'attribute' => 'created_at',
'format' => ['datetime', 'php:d.m.Y H:i'],
],
[
'attribute' => 'updated_at',
'format' => ['datetime', 'php:d.m.Y H:i'],
],
],
]
) ?>
</div>
</div>
</div>

25
common/bootstrap/SetUp.php

@ -4,8 +4,10 @@ namespace common\bootstrap;
//use Elasticsearch\Client; //use Elasticsearch\Client;
//use Elasticsearch\ClientBuilder; //use Elasticsearch\ClientBuilder;
use League\Flysystem\Adapter\Ftp; //use core\components\modules\ModuleInterface;
use League\Flysystem\Filesystem; use core\entities\ModuleRecord;
//use League\Flysystem\Adapter\Ftp;
//use League\Flysystem\Filesystem;
//use shop\cart\Cart; //use shop\cart\Cart;
//use shop\cart\cost\calculator\DynamicCost; //use shop\cart\cost\calculator\DynamicCost;
//use shop\cart\cost\calculator\SimpleCost; //use shop\cart\cost\calculator\SimpleCost;
@ -14,13 +16,9 @@ use core\dispatchers\AsyncEventDispatcher;
use core\dispatchers\DeferredEventDispatcher; use core\dispatchers\DeferredEventDispatcher;
use core\dispatchers\EventDispatcher; use core\dispatchers\EventDispatcher;
use core\dispatchers\SimpleEventDispatcher; use core\dispatchers\SimpleEventDispatcher;
use core\entities\behaviors\FlySystemImageUploadBehavior; //use core\entities\behaviors\FlySystemImageUploadBehavior;
//use shop\entities\Shop\Product\events\ProductAppearedInStock; //use shop\entities\Shop\Product\events\ProductAppearedInStock;
use core\jobs\AsyncEventJobHandler; use core\jobs\AsyncEventJobHandler;
//use shop\listeners\Shop\Category\CategoryPersistenceListener;
//use shop\listeners\Shop\Product\ProductAppearedInStockListener;
//use shop\listeners\Shop\Product\ProductSearchPersistListener;
//use shop\listeners\Shop\Product\ProductSearchRemoveListener;
use core\listeners\user\UserSignupConfirmedListener; use core\listeners\user\UserSignupConfirmedListener;
use core\listeners\user\UserSignupRequestedListener; use core\listeners\user\UserSignupRequestedListener;
//use shop\repositories\events\EntityPersisted; //use shop\repositories\events\EntityPersisted;
@ -43,7 +41,7 @@ use yii\di\Instance;
use yii\mail\MailerInterface; use yii\mail\MailerInterface;
use yii\rbac\ManagerInterface; use yii\rbac\ManagerInterface;
use yii\queue\Queue; use yii\queue\Queue;
use yiidreamteam\upload\ImageUploadBehavior; use Yii;
class SetUp implements BootstrapInterface class SetUp implements BootstrapInterface
{ {
@ -137,5 +135,16 @@ class SetUp implements BootstrapInterface
$container->set(ImageUploadBehavior::class, FlySystemImageUploadBehavior::class); $container->set(ImageUploadBehavior::class, FlySystemImageUploadBehavior::class);
*/ */
// Connect common modules
if (Yii::$app->db->schema->getTableSchema('{{%modules}}') !== null) {
$modules = ModuleRecord::find()->andWhere(['type' => 'common'])->andWhere(['active' => 1])->all();
foreach ($modules as $module) {
Yii::$app->setModule($module->name, [
'class' => $module->class,
]);
Yii::$app->getModule($module->name)->bootstrap(Yii::$app);
}
}
} }
} }

7
common/config/params.php

@ -1,12 +1,5 @@
<?php <?php
return [ return [
'supportEmail' => 'info@zertex.ru',
'adminEmail' => 'info@zertex.ru',
'user.passwordResetTokenExpire' => 3600, 'user.passwordResetTokenExpire' => 3600,
'user.rememberMeDuration' => 3600 * 24 * 30, 'user.rememberMeDuration' => 3600 * 24 * 30,
//'cookieDomain' => '.example.com',
//'frontendHostInfo' => 'http://example.com',
//'backendHostInfo' => 'http://backend.example.com',
//'staticHostInfo' => 'http://static.example.com',
'staticPath' => dirname(__DIR__, 2) . '/static',
]; ];

93
common/modules/blog/BlogModule.php

@ -0,0 +1,93 @@
<?php
namespace common\modules\blog;
use core\components\modules\ModuleInterface;
use yii\helpers\ArrayHelper;
/**
* blog module definition class
*/
class BlogModule extends \yii\base\Module implements ModuleInterface
{
/**
* @inheritdoc
*/
public $controllerNamespace = 'common\modules\blog\controllers';
/**
* @inheritdoc
*/
public function init()
{
parent::init();
// custom initialization code goes here
}
public function bootstrap($app)
{
// add rules
$app->getUrlManager()->addRules([
'blog' => 'blog/post/index',
]);
$app->getUrlManager()->addRules([
['class' => 'common\modules\blog\urls\BlogMainUrlRule'],
['class' => 'common\modules\blog\urls\BlogCategoryUrlRule'],
['class' => 'common\modules\blog\urls\BlogTagUrlRule'],
]);
// add languages
$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',
],
]);
// add menu items
/*$app->params['adminMenu'][] = [
'label' => \Yii::t('blog', 'Blog'), 'icon' => 'book', 'items' => [
['label' => \Yii::t('blog', 'Categories'), 'icon' => 'caret-right', 'url' => ['/blog/manage/category/index'], 'active' => \Yii::$app->controller->getUniqueId() == 'blog/manage/category'],
['label' => \Yii::t('blog', 'Posts'), 'icon' => 'caret-right', 'url' => ['/blog/manage/post/index'], 'active' => \Yii::$app->controller->getUniqueId() == 'blog/manage/post'],
], 'visible' => \Yii::$app->user->can('admin') || \Yii::$app->user->can('BlogManagement')
];*/
//print_r(basename($app->getBasePath())); die;
//print_r($app->basePath); die;
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' ]
],
[
'label' => \Yii::t( 'blog', 'Tags' ),
'icon' => 'caret-right',
'url' => [ '/blog/manage/tag/index' ]
],
],
'visible' => \Yii::$app->user->can( 'admin' ) || \Yii::$app->user->can( 'BlogManagement' )
];
}
}
}

162
common/modules/blog/controllers/PostController.php

@ -0,0 +1,162 @@
<?php
namespace common\modules\blog\controllers;
use common\modules\blog\forms\BlogCommentForm;
use common\modules\blog\repositories\read\BlogCategoryReadRepository;
use common\modules\blog\repositories\read\BlogPostReadRepository;
use common\modules\blog\repositories\read\BlogTagReadRepository;
use common\modules\blog\services\BlogCommentService;
use frontend\components\FrontendController;
use Yii;
use yii\data\ActiveDataProvider;
use yii\web\NotFoundHttpException;
use yii\filters\AccessControl;
class PostController extends FrontendController
{
public $layout = 'blog';
private $service;
private $posts;
private $categories;
private $tags;
public function __construct(
$id,
$module,
BlogCommentService $service,
BlogPostReadRepository $posts,
BlogCategoryReadRepository $categories,
BlogTagReadRepository $tags,
$config = []
)
{
parent::__construct($id, $module, $config);
$this->service = $service;
$this->posts = $posts;
$this->categories = $categories;
$this->tags = $tags;
}
public function behaviors(): array
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['index', 'category', 'tag', 'post'],
'allow' => true,
//'roles' => ['Blog'],
],
[
'actions' => ['comment'],
'allow' => true,
'roles' => ['Comments'],
],
[ // all the action are accessible to admin
'allow' => true,
'roles' => ['admin'],
],
],
],
];
}
/**
* @return string
*/
public function actionIndex()
{
/* @var $dataProvider ActiveDataProvider */
$dataProvider = $this->posts->getAll();
$dataProvider->pagination->pageSize = 10;
return $this->render('index', [
'dataProvider' => $dataProvider,
]);
}
/**
* @param $id
*
* @return string
* @throws NotFoundHttpException
*/
public function actionCategory($id)
{
if (!$category = $this->categories->find($id)) {
throw new NotFoundHttpException('The requested page does not exist.');
}
$dataProvider = $this->posts->getAllByCategory($category);
return $this->render('category', [
'category' => $category,
'dataProvider' => $dataProvider,
]);
}
/**
* @param $id
* @return mixed
* @throws NotFoundHttpException
*/
public function actionTag($id)
{
if (!$tag = $this->tags->find($id)) {
throw new NotFoundHttpException('The requested page does not exist.');
}
$dataProvider = $this->posts->getAllByTag($tag);
return $this->render('tag', [
'tag' => $tag,
'dataProvider' => $dataProvider,
]);
}
/**
* @param $id
* @return mixed
* @throws NotFoundHttpException
*/
public function actionPost($id)
{
if (!$post = $this->posts->find($id)) {
throw new NotFoundHttpException('The requested page does not exist.');
}
return $this->render('post', [
'post' => $post,
]);
}
/**
* @param $id
* @return mixed
* @throws NotFoundHttpException
*/
public function actionComment($id)
{
if (!$post = $this->posts->find($id)) {
throw new NotFoundHttpException('The requested page does not exist.');
}
$form = new BlogCommentForm();
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$comment = $this->service->create($post->id, Yii::$app->user->id, $form);
return $this->redirect(['post', 'id' => $post->id, '#' => 'comment_' . $comment->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('comment', [
'post' => $post,
'model' => $form,
]);
}
}

155
common/modules/blog/controllers/manage/CategoryController.php

@ -0,0 +1,155 @@
<?php
namespace common\modules\blog\controllers\manage;
use common\modules\blog\entities\BlogCategory;
use common\modules\blog\forms\BlogCategoryForm;
use common\modules\blog\forms\search\BlogCategorySearch;
use common\modules\blog\services\BlogCategoryManageService;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
class CategoryController extends Controller
{
private $service;
public function __construct($id, $module, BlogCategoryManageService $service, $config = [])
{
parent::__construct($id, $module, $config);
$this->service = $service;
}
public function behaviors(): array
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['BlogManagement'],
],
[ // all the action are accessible to admin
'allow' => true,
'roles' => ['admin'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* @return string
*/
public function actionIndex()
{
$searchModel = new BlogCategorySearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* @param $id
*
* @return string
* @throws NotFoundHttpException
*/
public function actionView($id)
{
$category = $this->findModel($id);
return $this->render('view', [
'category' => $category,
]);
}
/**
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$form = new BlogCategoryForm();
$form->updateSort();
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$category = $this->service->create($form);
return $this->redirect(['view', 'id' => $category->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('create', [
'model' => $form,
]);
}
/**
* @param $id
*
* @return string|\yii\web\Response
* @throws NotFoundHttpException
*/
public function actionUpdate($id)
{
$category = $this->findModel($id);
$form = new BlogCategoryForm($category);
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$this->service->edit($category->id, $form);
return $this->redirect(['view', 'id' => $category->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('update', [
'model' => $form,
'category' => $category,
]);
}
/**
* @param $id
*
* @return \yii\web\Response
*/
public function actionDelete($id)
{
//$category = $this->findModel($id);
try {
$this->service->remove($id);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
return $this->redirect(['index']);
}
/**
* @param $id
*
* @return BlogCategory
* @throws NotFoundHttpException
*/
protected function findModel($id): BlogCategory
{
if (($model = BlogCategory::findOne($id)) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
}

159
common/modules/blog/controllers/manage/CommentController.php

@ -0,0 +1,159 @@
<?php
namespace common\modules\blog\controllers\manage;
use common\modules\blog\entities\BlogPost;
use common\modules\blog\forms\BlogCommentEditForm;
use common\modules\blog\forms\search\BlogCommentSearch;
use common\modules\blog\services\BlogCommentManageService;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
class CommentController extends Controller
{
private $service;
public function __construct($id, $module, BlogCommentManageService $service, $config = [])
{
parent::__construct($id, $module, $config);
$this->service = $service;
}
public function behaviors(): array
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['BlogManagement'],
],
[ // all the action are accessible to admin
'allow' => true,
'roles' => ['admin'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* @return mixed
*/
public function actionIndex()
{
$searchModel = new BlogCommentSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* @param $post_id
* @param $id
*
* @return string|\yii\web\Response
* @throws NotFoundHttpException
*/
public function actionUpdate($post_id, $id)
{
$post = $this->findModel($post_id);
$comment = $post->getComment($id);
$form = new BlogCommentEditForm($comment);
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$this->service->edit($post->id, $comment->id, $form);
return $this->redirect(['view', 'post_id' => $post->id, 'id' => $comment->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('update', [
'post' => $post,
'model' => $form,
]);
}
/**
* @param $post_id
* @param $id
*
* @return string
* @throws NotFoundHttpException
*/
public function actionView($post_id, $id)
{
$post = $this->findModel($post_id);
$comment = $post->getComment($id);
return $this->render('view', [
'post' => $post,
'comment' => $comment,
]);
}
/**
* @param $post_id
* @param $id
*
* @return \yii\web\Response
* @throws NotFoundHttpException
*/
public function actionActivate($post_id, $id)
{
$post = $this->findModel($post_id);
try {
$this->service->activate($post->id, $id);
} catch (\DomainException $e) {
Yii::$app->session->setFlash('error', $e->getMessage());
}
return $this->redirect(['view', 'post_id' => $post_id, 'id' => $id]);
}
/**
* @param $post_id
* @param $id
*
* @return \yii\web\Response
* @throws NotFoundHttpException
*/
public function actionDelete($post_id, $id)
{
$post = $this->findModel($post_id);
try {
$this->service->remove($post->id, $id);
} catch (\DomainException $e) {
Yii::$app->session->setFlash('error', $e->getMessage());
}
return $this->redirect(['index']);
}
/**
* @param $id
*
* @return BlogPost
* @throws NotFoundHttpException
*/
protected function findModel($id): BlogPost
{
if (($model = BlogPost::findOne($id)) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
}

202
common/modules/blog/controllers/manage/PostController.php

@ -0,0 +1,202 @@
<?php
namespace common\modules\blog\controllers\manage;
use common\modules\blog\entities\BlogPost;
use common\modules\blog\entities\BlogTag;
use common\modules\blog\forms\BlogPostForm;
use common\modules\blog\forms\search\BlogPostSearch;
use common\modules\blog\services\BlogPostManageService;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
use yii\web\Response;
class PostController extends Controller
{
private $service;
public function __construct($id, $module, BlogPostManageService $service, $config = [])
{
parent::__construct($id, $module, $config);
$this->service = $service;
}
public function behaviors(): array
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['BlogManagement'],
],
[ // all the action are accessible to admin
'allow' => true,
'roles' => ['admin'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
'activate' => ['POST'],
'draft' => ['POST'],
'delete-photo' => ['POST'],
'move-photo-up' => ['POST'],
'move-photo-down' => ['POST'],
],
],
];
}
/**
* @return string
*/
public function actionIndex()
{
$searchModel = new BlogPostSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render( 'index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* @param $id
*
* @return string
* @throws NotFoundHttpException
*/
public function actionView($id)
{
$post = $this->findModel($id);
return $this->render('view', [
'post' => $post,
]);
}
/**
* @return string|Response
*/
public function actionCreate()
{
$form = new BlogPostForm();
$form->published_at = date('d.m.Y H:i:s');
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$post = $this->service->create($form);
return $this->redirect(['view', 'id' => $post->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('create', [
'model' => $form,
]);
}
/**
* @param $id
*
* @return string|\yii\web\Response
* @throws NotFoundHttpException
*/
public function actionUpdate($id)
{
$post = $this->findModel($id);
$form = new BlogPostForm($post);
$form->published_at = date('d.m.Y H:i:s', $form->published_at);
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$this->service->edit($post->id, $form);
return $this->redirect(['view', 'id' => $post->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('update', [
'model' => $form,
'post' => $post,
]);
}
/**
* @param $id
*
* @return Response
*/
public function actionDelete($id)
{
try {
$this->service->remove($id);
} catch (\DomainException $e) {
Yii::$app->session->setFlash('error', $e->getMessage());
}
return $this->redirect(['index']);
}
/**
* @param integer $id
* @return mixed
*/
public function actionActivate($id)
{
try {
$this->service->activate($id);
} catch (\DomainException $e) {
Yii::$app->session->setFlash('error', $e->getMessage());
}
return $this->redirect(['view', 'id' => $id]);
}
/**
* @param integer $id
* @return mixed
*/
public function actionDraft($id)
{
try {
$this->service->draft($id);
} catch (\DomainException $e) {
Yii::$app->session->setFlash('error', $e->getMessage());
}
return $this->redirect(['view', 'id' => $id]);
}
public function actionTagSearch($q = null, $id = null)
{
\Yii::$app->response->format = Response::FORMAT_JSON;
$out = ['results' => ['id' => '', 'text' => '']];
if (!is_null($q)) {
$data = BlogTag::find()->select('name as id, name as text')->andWhere(['like', 'name', $q])->orderBy('name')->limit(20)->asArray()->all();
$out['results'] = array_values($data);
}
elseif ($id > 0) {
$tag_name = BlogTag::findOne($id)->name;
$out['results'] = ['id' => $tag_name, 'text' => $tag_name];
}
return $out;
}
/**
* @param $id
*
* @return BlogPost
* @throws NotFoundHttpException
*/
protected function findModel($id): BlogPost
{
if (($model = BlogPost::findOne($id)) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
}

151
common/modules/blog/controllers/manage/TagController.php

@ -0,0 +1,151 @@
<?php
namespace common\modules\blog\controllers\manage;
use common\modules\blog\entities\BlogTag;
use common\modules\blog\forms\BlogTagSingleForm;
use common\modules\blog\forms\search\BlogTagSearch;
use common\modules\blog\services\BlogTagManageService;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
class TagController extends Controller
{
private $service;
public function __construct($id, $module, BlogTagManageService $service, $config = [])
{
parent::__construct($id, $module, $config);
$this->service = $service;
}
public function behaviors(): array
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['BlogManagement'],
],
[ // all the action are accessible to admin
'allow' => true,
'roles' => ['admin'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* @return string
*/
public function actionIndex()
{
$searchModel = new BlogTagSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* @param $id
*
* @return string
* @throws NotFoundHttpException
*/
public function actionView($id)
{
$tag = $this->findModel($id);
return $this->render('view', [
'tag' => $tag,
]);
}
/**
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$form = new BlogTagSingleForm();
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$tag = $this->service->create($form);
return $this->redirect(['view', 'id' => $tag->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('create', [
'model' => $form,
]);
}
/**
* @param $id
*
* @return string|\yii\web\Response
* @throws NotFoundHttpException
*/
public function actionUpdate($id)
{
$tag = $this->findModel($id);
$form = new BlogTagSingleForm($tag);
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$this->service->edit($tag->id, $form);
return $this->redirect(['view', 'id' => $tag->id]);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
}
return $this->render('update', [
'model' => $form,
'tag' => $tag,
]);
}
/**
* @param $id
*
* @return \yii\web\Response
*/
public function actionDelete($id)
{
try {
$this->service->remove($id);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', $e->getMessage());
}
return $this->redirect(['index']);
}
/**
* @param $id
*
* @return BlogTag
* @throws NotFoundHttpException
*/
protected function findModel($id): BlogTag
{
if (($model = BlogTag::findOne($id)) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
}

88
common/modules/blog/entities/BlogCategory.php

@ -0,0 +1,88 @@
<?php
namespace common\modules\blog\entities;
use core\behaviors\MetaBehavior;
use core\entities\Meta;
use yii\db\ActiveRecord;
use Yii;
/**
* @property integer $id
* @property string $name
* @property string $slug
* @property string $title
* @property string $description
* @property integer $sort
* @property string $meta_json
* @property Meta $meta
*/
class BlogCategory extends ActiveRecord
{
public $meta;
public static function create($name, $slug, $title, $description, $sort, Meta $meta): self
{
$category = new static();
$category->name = $name;
$category->slug = $slug;
$category->title = $title;
$category->description = $description;
$category->sort = $sort;
$category->meta = $meta;
return $category;
}
public function edit($name, $slug, $title, $description, $sort, Meta $meta): void
{
$this->name = $name;
$this->slug = $slug;
$this->title = $title;
$this->description = $description;
$this->sort = $sort;
$this->meta = $meta;
}
public function attributeLabels()
{
return [
'id' => Yii::t('blog', 'ID'),
'name' => Yii::t('blog', 'Name'),
'slug' => Yii::t('blog', 'SEO link'),
'sort' => Yii::t('blog', 'Sort'),
'title' => Yii::t('blog', 'Title'),
'description' => Yii::t('blog', 'Description'),
'meta.title' => Yii::t('blog', 'Meta Title'),
'meta.description' => Yii::t('blog', 'Meta Description'),
'meta.keywords' => Yii::t('blog', 'Meta Keywords'),
];
}
public function getSeoTitle(): string
{
return $this->meta->title ?: $this->getHeadingTile();
}
public function getHeadingTile(): string
{
return $this->title ?: $this->name;
}
public function getPostsCount(): int
{
return BlogPost::find()->where(['category_id' => $this->id])->count('*');
}
public static function tableName(): string
{
return '{{%blog_categories}}';
}
public function behaviors(): array
{
return [
MetaBehavior::className(),
];
}
}

93
common/modules/blog/entities/BlogComment.php

@ -0,0 +1,93 @@
<?php
namespace common\modules\blog\entities;
use core\entities\user\User;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
use Yii;
/**
* @property int $id
* @property int $created_at
* @property int $post_id
* @property int $user_id
* @property int $parent_id
* @property string $text
* @property bool $active
*
* @property BlogPost $post
*/
class BlogComment extends ActiveRecord
{
public static function create($userId, $parentId, $text): self
{
$review = new static();
$review->user_id = $userId;
$review->parent_id = $parentId;
$review->text = $text;
$review->created_at = time();
$review->active = true;
return $review;
}
public function edit($parentId, $text): void
{
$this->parent_id = $parentId;
$this->text = $text;
}
public function attributeLabels()
{
return [
'id' => Yii::t('blog', 'ID'),
'user_id' => Yii::t('blog', 'User'),
'parent_id' => Yii::t('blog', 'Parent Comment ID'),
'created_at' => Yii::t('blog', 'Created At'),
'active' => Yii::t('blog', 'Published'),
'post_id' => Yii::t('blog', 'Post'),
'text' => Yii::t('blog', 'Comment'),
];
}
public function activate(): void
{
$this->active = true;
}
public function draft(): void
{
$this->active = false;
}
public function isActive(): bool
{
return $this->active == true;
}
public function isIdEqualTo($id): bool
{
return $this->id == $id;
}
public function isChildOf($id): bool
{
return $this->parent_id == $id;
}
public function getPost(): ActiveQuery
{
return $this->hasOne(BlogPost::class, ['id' => 'post_id']);
}
public function getUser(): ActiveQuery
{
return $this->hasOne(User::class, ['id' => 'user_id']);
}
public static function tableName(): string
{
return '{{%blog_comments}}';
}
}

347
common/modules/blog/entities/BlogPost.php

@ -0,0 +1,347 @@
<?php
namespace common\modules\blog\entities;
use common\modules\blog\entities\queries\BlogPostQuery;
use core\behaviors\MetaBehavior;
use core\entities\Meta;
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
use yii\web\UploadedFile;
use yiidreamteam\upload\ImageUploadBehavior;
use Yii;
/**
* This is the model class for table "posts".
*
* @property int $id
* @property int $category_id
* @property int $published_at
* @property int $created_at
* @property int $updated_at
* @property string $title
* @property string $description
* @property string $content
* @property string $image
* @property string $video
* @property int $status
* @property string $meta_json
* @property int $comments_count
* @property int $views
* @property string $slug
*
* @property BlogComment[] $blogComments
* @property BlogTagAssignment[] $blogTagAssignments
* @property BlogTag[] $tags
* @property BlogCategory $category
*
* @mixin ImageUploadBehavior
*/
class BlogPost extends ActiveRecord
{
const STATUS_DRAFT = 0;
const STATUS_ACTIVE = 1;
const FILE_ORIGINAL_PATH = '@staticRoot/origin/posts';
public $meta;
public static function create($categoryId, $title, $slug, $description, $content, $published_at, $video, Meta $meta): self
{
$post = new static();
$post->category_id = $categoryId;
$post->title = $title;
$post->slug = $slug;
$post->description = $description;
$post->content = $content;
$post->meta = $meta;
$post->status = self::STATUS_DRAFT;
$post->created_at = time();
$post->comments_count = 0;
$post->published_at = $published_at;
$post->video = $video;
return $post;
}
public function setImage(UploadedFile $image): void
{
$this->image = $image;
}
public function edit($categoryId, $title, $slug, $description, $content, $published_at, $video, Meta $meta): void
{
$this->category_id = $categoryId;
$this->title = $title;
$this->slug = $slug;
$this->description = $description;
$this->content = $content;
$this->meta = $meta;
$this->published_at = $published_at;
$this->video = $video;
}
/**
* @inheritdoc
*/
public static function tableName()
{
return 'blog_posts';
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('blog', 'ID'),
'category_id' => Yii::t('blog', 'Category'),
'published_at' => Yii::t('blog', 'Published At'),
'created_at' => Yii::t('blog', 'Created At'),
'updated_at' => Yii::t('blog', 'Updated At'),
'title' => Yii::t('blog', 'Title'),
'description' => Yii::t('blog', 'Description'),
'content' => Yii::t('blog', 'Content'),
'image' => Yii::t('blog', 'Image'),
'video' => Yii::t('blog', 'Video'),
'status' => Yii::t('blog', 'Status'),
'meta_json' => Yii::t('blog', 'Meta Json'),
'comments_count' => Yii::t('blog', 'Comments Count'),
'views' => Yii::t('blog', 'Views'),
'slug' => Yii::t('blog', 'Slug'),
'meta.title' => Yii::t('blog', 'Meta Title'),
'meta.description' => Yii::t('blog', 'Meta Description'),
'meta.keywords' => Yii::t('blog', 'Meta Keywords'),
];
}
public function activate(): void
{
if ($this->isActive()) {
throw new \DomainException('Post is already active.');
}
$this->status = self::STATUS_ACTIVE;
}
public function draft(): void
{
if ($this->isDraft()) {
throw new \DomainException('Post is already draft.');
}
$this->status = self::STATUS_DRAFT;
}
public function isActive(): bool
{
return $this->status == self::STATUS_ACTIVE;
}
public function isDraft(): bool
{
return $this->status == self::STATUS_DRAFT;
}
public function getSeoTitle(): string
{
return $this->meta->title ?: $this->title;
}
// Tags
public function assignTag($id): void
{
$assignments = $this->blogTagAssignments;
foreach ($assignments as $assignment) {
if ($assignment->isForTag($id)) {
return;
}
}
$assignments[] = BlogTagAssignment::create($id);
$this->blogTagAssignments = $assignments;
}
public function revokeTag($id): void
{
$assignments = $this->blogTagAssignments;
foreach ($assignments as $i => $assignment) {
if ($assignment->isForTag($id)) {
unset($assignments[$i]);
$this->blogTagAssignments = $assignments;
return;
}
}
throw new \DomainException('Assignment is not found.');
}
public function revokeTags(): void
{
$this->blogTagAssignments = [];
}
// Comments
public function addComment($userId, $parentId, $text): BlogComment
{
$parent = $parentId ? $this->getComment($parentId) : null;
if ($parent && !$parent->isActive()) {
throw new \DomainException('Cannot add comment to inactive parent.');
}
$comments = $this->blogComments;
$comments[] = $comment = BlogComment::create($userId, $parent ? $parent->id : null, $text);
$this->updateComments($comments);
return $comment;
}
public function editComment($id, $parentId, $text): void
{
$parent = $parentId ? $this->getComment($parentId) : null;
$comments = $this->blogComments;
foreach ($comments as $comment) {
if ($comment->isIdEqualTo($id)) {
$comment->edit($parent ? $parent->id : null, $text);
$this->updateComments($comments);
return;
}
}
throw new \DomainException('Comment is not found.');
}
public function activateComment($id): void
{
$comments = $this->blogComments;
foreach ($comments as $comment) {
if ($comment->isIdEqualTo($id)) {
$comment->activate();
$this->updateComments($comments);
return;
}
}
throw new \DomainException('Comment is not found.');
}
public function removeComment($id): void
{
$comments = $this->blogComments;
foreach ($comments as $i => $comment) {
if ($comment->isIdEqualTo($id)) {
if ($this->hasChildren($comment->id)) {
$comment->draft();
} else {
unset($comments[$i]);
}
$this->updateComments($comments);
return;
}
}
throw new \DomainException('Comment is not found.');
}
public function getComment($id): BlogComment
{
foreach ($this->blogComments as $comment) {
if ($comment->isIdEqualTo($id)) {
return $comment;
}
}
throw new \DomainException('Comment is not found.');
}
private function hasChildren($id): bool
{
foreach ($this->blogComments as $comment) {
if ($comment->isChildOf($id)) {
return true;
}
}
return false;
}
private function updateComments(array $comments): void
{
$this->blogComments = $comments;
$this->comments_count = count(array_filter($comments, function (BlogComment $comment) {
return $comment->isActive();
}));
}
######################################
public function getBlogComments(): ActiveQuery
{
return $this->hasMany(BlogComment::className(), ['post_id' => 'id']);
}
public function getBlogTagAssignments(): ActiveQuery
{
return $this->hasMany(BlogTagAssignment::className(), ['post_id' => 'id']);
}
public function getTags(): ActiveQuery
{
return $this->hasMany(BlogTag::className(), ['id' => 'tag_id'])->viaTable('post_tag_assignments', ['post_id' => 'id']);
}
public function getCategory(): ActiveQuery
{
return $this->hasOne(BlogCategory::className(), ['id' => 'category_id']);
}
######################################
public function behaviors(): array
{
return [
TimestampBehavior::className(),
MetaBehavior::className(),
[
'class' => SaveRelationsBehavior::className(),
'relations' => ['blogTagAssignments', 'blogComments'],
],
[
// todo Image Sizes to settings or theme settings
'class' => ImageUploadBehavior::className(),
'attribute' => 'image',
'createThumbsOnRequest' => true,
//'filePath' => $this::FILE_ORIGINAL_PATH . '/[[id]].[[extension]]',
'filePath' => '@staticRoot/origin/posts/[[id]].[[extension]]',
'fileUrl' => '@static/origin/posts/[[id]].[[extension]]',
'thumbPath' => '@staticRoot/cache/posts/[[profile]]_[[id]].[[extension]]',
'thumbUrl' => '@static/cache/posts/[[profile]]_[[id]].[[extension]]',
'thumbs' => [
'blog_list' => ['width' => 750, 'height' => 300],
'blog_post' => ['width' => 900, 'height' => 300],
'admin' => ['width' => 60, 'height' => 60],
'thumb' => ['width' => 150, 'height' => 150],
'list' => ['width' => 200, 'height' => 200],
'home_slider' => ['width' => 369, 'height' => 343],
'94_94' => ['width' => 94, 'height' => 94],
'368_287' => ['width' => 368, 'height' => 287],
'370_325' => ['width' => 370, 'height' => 325],
'683_407' => ['width' => 683, 'height' => 407],
'thumb_gallery_view' => ['width' => 300, 'height' => 170],
//'widget_list' => ['width' => 228, 'height' => 228],
//'origin' => ['processor' => [new WaterMarker(1024, 768, '@frontend/web/image/logo.png'), 'process']],
'origin' => ['width' => 1024, 'height' => 768],
],
],
];
}
public function transactions(): array
{
return [
self::SCENARIO_DEFAULT => self::OP_ALL,
];
}
public static function find(): BlogPostQuery
{
return new BlogPostQuery(static::class);
}
}

46
common/modules/blog/entities/BlogTag.php

@ -0,0 +1,46 @@
<?php
namespace common\modules\blog\entities;
use yii\db\ActiveRecord;
use yii\caching\TagDependency;
use Yii;
/**
* @property integer $id
* @property string $name
* @property string $slug
*/
class BlogTag extends ActiveRecord
{
public static function create($name, $slug): self
{
$tag = new static();
$tag->name = $name;
$tag->slug = $slug;
return $tag;
}
public function edit($name, $slug): void
{
$this->name = $name;
if ($slug != $this->slug)
{
TagDependency::invalidate(\Yii::$app->cache, 'blog_tags');
}
$this->slug = $slug;
}
public function attributeLabels()
{
return [
'name' => Yii::t('blog', 'Tag Name'),
'slug' => Yii::t('blog', 'SEO link'),
];
}
public static function tableName(): string
{
return '{{%blog_tags}}';
}
}

29
common/modules/blog/entities/BlogTagAssignment.php

@ -0,0 +1,29 @@
<?php
namespace common\modules\blog\entities;
use yii\db\ActiveRecord;
/**
* @property integer $post_id;
* @property integer $tag_id;
*/
class BlogTagAssignment extends ActiveRecord
{
public static function create($tagId): self
{
$assignment = new static();
$assignment->tag_id = $tagId;
return $assignment;
}
public function isForTag($id): bool
{
return $this->tag_id == $id;
}
public static function tableName(): string
{
return '{{%blog_tag_assignments}}';
}
}

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

@ -0,0 +1,35 @@
<?php
namespace common\modules\blog\entities\queries;
use common\modules\blog\entities\BlogPost;
use yii\db\ActiveQuery;
class BlogPostQuery extends ActiveQuery
{
/**
* @param null $alias
* @return $this
*/
public function active($alias = null)
{
return $this->andWhere([
($alias ? $alias . '.' : '') . 'status' => BlogPost::STATUS_ACTIVE,
]);
}
public function byType($type)
{
return $this->andWhere(['type_id' => $type]);
}
public function last()
{
return $this->orderBy(['published_at' => SORT_DESC]);
}
public function popular()
{
return $this->orderBy(['views' => SORT_DESC]);
}
}

71
common/modules/blog/forms/BlogCategoryForm.php

@ -0,0 +1,71 @@
<?php
namespace common\modules\blog\forms;
use common\modules\blog\entities\BlogCategory;
use core\forms\CompositeForm;
use core\forms\MetaForm;
use core\validators\SlugValidator;
use Yii;
/**
* @property MetaForm $meta;
*/
class BlogCategoryForm extends CompositeForm
{
public $name;
public $slug;
public $title;
public $description;
public $sort;
private $_category;
public function __construct(BlogCategory $category = null, $config = [])
{
if ($category) {
$this->name = $category->name;
$this->slug = $category->slug;
$this->title = $category->title;
$this->description = $category->description;
$this->sort = $category->sort;
$this->meta = new MetaForm($category->meta);
$this->_category = $category;
} else {
$this->meta = new MetaForm();
$this->sort = BlogCategory::find()->max('sort') + 1;
}
parent::__construct($config);
}
public function rules(): array
{
return [
[['name', 'slug'], 'required'],
[['name', 'slug', 'title'], 'string', 'max' => 255],
[['description'], 'string'],
['slug', SlugValidator::class],
[['name', 'slug'], 'unique', 'targetClass' => BlogCategory::class, 'filter' => $this->_category ? ['<>', 'id', $this->_category->id] : null]
];
}
public function attributeLabels()
{
return [
'name' => Yii::t('blog', 'Name'),
'slug' => Yii::t('blog', 'SEO link'),
'sort' => Yii::t('blog', 'Sort'),
'title' => Yii::t('blog', 'Title'),
'description' => Yii::t('blog', 'Description'),
];
}
public function updateSort() {
$this->sort = BlogCategory::find()->max('sort') + 1;
}
public function internalForms(): array
{
return ['meta'];
}
}

37
common/modules/blog/forms/BlogCommentEditForm.php

@ -0,0 +1,37 @@
<?php
namespace common\modules\blog\forms;
use common\modules\blog\entities\BlogComment;
use yii\base\Model;
use Yii;
class BlogCommentEditForm extends Model
{
public $parentId;
public $text;
public function __construct(BlogComment $comment, $config = [])
{
$this->parentId = $comment->parent_id;
$this->text = $comment->text;
parent::__construct($config);
}
public function rules(): array
{
return [
[['text'], 'required'],
['text', 'string'],
['parentId', 'integer'],
];
}
public function attributeLabels()
{
return [
'parentId' => Yii::t('blog', 'Parent Comment ID'),
'text' => Yii::t('blog', 'Comment'),
];
}
}

20
common/modules/blog/forms/BlogCommentForm.php

@ -0,0 +1,20 @@
<?php
namespace common\modules\blog\forms;
use yii\base\Model;
class BlogCommentForm extends Model
{
public $parentId;
public $text;
public function rules(): array
{
return [
[['text'], 'required'],
['text', 'string'],
['parentId', 'integer'],
];
}
}

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

@ -0,0 +1,108 @@
<?php
namespace common\modules\blog\forms;
use common\modules\blog\entities\BlogCategory;
use common\modules\blog\entities\BlogPost;
use core\forms\CompositeForm;
use core\forms\MetaForm;
use core\validators\SlugValidator;
use yii\helpers\ArrayHelper;
use yii\web\UploadedFile;
use Yii;
/**
* @property MetaForm $meta
* @property BlogTagForm $tags
*/
class BlogPostForm extends CompositeForm
{
public $category_id;
public $title;
public $description;
public $content;
public $image;
public $video;
public $published_at;
public $slug;
public $reset_image;
public $_post;
public function __construct(BlogPost $post = null, $config = [])
{
if ($post) {
$this->category_id = $post->category_id;
$this->title = $post->title;
$this->description = $post->description;
$this->content = $post->content;
$this->video = $post->video;
$this->published_at = $post->published_at;
$this->slug = $post->slug;
$this->meta = new MetaForm($post->meta);
$this->tags = new BlogTagForm($post);
$this->_post = $post;
} else {
$this->meta = new MetaForm();
$this->tags = new BlogTagForm();
}
parent::__construct($config);
}
public function rules(): array
{
return [
[['category_id', 'title'], 'required'],
[['title', 'video'], 'string', 'max' => 255],
[['category_id'], 'integer'],
[['description', 'content'], 'string'],
[['image'], 'image'],
['reset_image', 'boolean'],
['published_at', 'safe'],
['slug', SlugValidator::class],
[['slug'], 'unique', 'targetClass' => BlogPost::class, 'filter' => $this->_post ? ['<>', 'id', $this->_post->id] : null],
];
}
public function attributeLabels()
{
return [
'id' => Yii::t('blog', 'ID'),
'category_id' => Yii::t('blog', 'Category'),
'published_at' => Yii::t('blog', 'Published At'),
'created_at' => Yii::t('blog', 'Created At'),
'updated_at' => Yii::t('blog', 'Updated At'),
'title' => Yii::t('blog', 'Title'),
'description' => Yii::t('blog', 'Description'),
'content' => Yii::t('blog', 'Content'),
'image' => Yii::t('blog', 'Image'),
'video' => Yii::t('blog', 'Video'),
'status' => Yii::t('blog', 'Status'),
'meta_json' => Yii::t('blog', 'Meta Json'),
'comments_count' => Yii::t('blog', 'Comments Count'),
'views' => Yii::t('blog', 'Views'),
'slug' => Yii::t('blog', 'Slug'),
'reset_image' => Yii::t('blog', 'Reset Image'),
];
}
public function categoriesList(): array
{
return ArrayHelper::map(BlogCategory::find()->orderBy('sort')->asArray()->all(), 'id', 'name');
}
protected function internalForms(): array
{
return ['meta', 'tags'];
}
public function beforeValidate(): bool
{
if (parent::beforeValidate()) {
$this->image = UploadedFile::getInstance($this, 'image');
$this->published_at = strtotime($this->published_at);
return true;
}
return false;
}
}

46
common/modules/blog/forms/BlogTagForm.php

@ -0,0 +1,46 @@
<?php
namespace common\modules\blog\forms;
use common\modules\blog\entities\BlogPost;
use common\modules\blog\entities\BlogTag;
use yii\base\Model;
use yii\helpers\ArrayHelper;
/**
* @property array $newNames
*/
class BlogTagForm extends Model
{
public $existing = [];
public $textNew;
public $new_tags;
public function __construct(BlogPost $post = null, $config = [])
{
if ($post) {
$this->existing = ArrayHelper::getColumn($post->blogTagAssignments, 'tag_id');
}
parent::__construct($config);
}
public function rules(): array
{
return [
['existing', 'each', 'rule' => ['integer']],
[['textNew'], 'string'],
['existing', 'default', 'value' => []],
['new_tags', 'each', 'rule' => ['string']],
];
}
public function tagsList(): array
{
return ArrayHelper::map(BlogTag::find()->orderBy('name')->asArray()->all(), 'id', 'name');
}
public function getNewNames(): array
{
return array_filter(array_map('trim', preg_split('#\s*,\s*#i', $this->textNew)));
}
}

45
common/modules/blog/forms/BlogTagSingleForm.php

@ -0,0 +1,45 @@
<?php
namespace common\modules\blog\forms;
use common\modules\blog\entities\BlogTag;
use core\validators\SlugValidator;
use yii\base\Model;
use Yii;
class BlogTagSingleForm extends Model
{
public $name;
public $slug;
public $type_id;
private $_tag;
public function __construct(BlogTag $tag = null, $config = [])
{
if ($tag) {
$this->name = $tag->name;
$this->slug = $tag->slug;
$this->_tag = $tag;
}
parent::__construct($config);
}
public function rules(): array
{
return [
[['name', 'slug'], 'required'],
[['name', 'slug'], 'string', 'max' => 255],
['slug', SlugValidator::class],
[['name', 'slug'], 'unique', 'targetClass' => BlogTag::class, 'filter' => $this->_tag ? ['<>', 'id', $this->_tag->id] : null]
];
}
public function attributeLabels()
{
return [
'name' => Yii::t('blog', 'Tag Name'),
'slug' => Yii::t('blog', 'SEO link'),
];
}
}

57
common/modules/blog/forms/search/BlogCategorySearch.php

@ -0,0 +1,57 @@
<?php
namespace common\modules\blog\forms\search;
use common\modules\blog\entities\BlogCategory;
use yii\base\Model;
use yii\data\ActiveDataProvider;
class BlogCategorySearch extends Model
{
public $id;
public $name;
public $slug;
public $title;
public function rules(): array
{
return [
[['id'], 'integer'],
[['name', 'slug', 'title'], 'safe'],
];
}
/**
* @param array $params
* @return ActiveDataProvider
*/
public function search(array $params): ActiveDataProvider
{
$query = BlogCategory::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => ['sort' => SORT_ASC]
]
]);
$this->load($params);
if (!$this->validate()) {
$query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
]);
$query
->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'slug', $this->slug])
->andFilterWhere(['like', 'title', $this->title]);
return $dataProvider;
}
}

72
common/modules/blog/forms/search/BlogCommentSearch.php

@ -0,0 +1,72 @@
<?php
namespace common\modules\blog\forms\search;
use common\modules\blog\entities\BlogComment;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
class BlogCommentSearch extends Model
{
public $id;
public $text;
public $active;
public $post_id;
public function rules(): array
{
return [
[['id', 'post_id'], 'integer'],
[['text'], 'safe'],
[['active'], 'boolean'],
];
}
/**
* @param array $params
* @return ActiveDataProvider
*/
public function search(array $params): ActiveDataProvider
{
$query = BlogComment::find()->with(['post']);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'key' => function (BlogComment $comment) {
return [
'post_id' => $comment->post_id,
'id' => $comment->id,
];
},
'sort' => [
'defaultOrder' => ['id' => SORT_DESC]
]
]);
$this->load($params);
if (!$this->validate()) {
$query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
'post_id' => $this->post_id,
]);
$query
->andFilterWhere(['like', 'text', $this->text]);
return $dataProvider;
}
public function activeList(): array
{
return [
1 => Yii::$app->formatter->asBoolean(true),
0 => Yii::$app->formatter->asBoolean(false),
];
}
}

69
common/modules/blog/forms/search/BlogPostSearch.php

@ -0,0 +1,69 @@
<?php
namespace common\modules\blog\forms\search;
use common\modules\blog\entities\BlogCategory;
use common\modules\blog\entities\BlogPost;
use common\modules\blog\helpers\BlogPostHelper;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use yii\helpers\ArrayHelper;
class BlogPostSearch extends Model
{
public $id;
public $title;
public $status;
public $category_id;
public function rules(): array
{
return [
[['id', 'status', 'category_id'], 'integer'],
[['title'], 'safe'],
];
}
/**
* @param array $params
* @return ActiveDataProvider
*/
public function search(array $params): ActiveDataProvider
{
$query = BlogPost::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => ['id' => SORT_DESC]
]
]);
$this->load($params);
if (!$this->validate()) {
$query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
'category_id' => $this->category_id,
]);
$query
->andFilterWhere(['like', 'title', $this->title]);
return $dataProvider;
}
public function categoriesList(): array
{
return ArrayHelper::map(BlogCategory::find()->orderBy('sort')->asArray()->all(), 'id', 'title');
}
public function statusList(): array
{
return BlogPostHelper::statusList();
}
}

55
common/modules/blog/forms/search/BlogTagSearch.php

@ -0,0 +1,55 @@
<?php
namespace common\modules\blog\forms\search;
use common\modules\blog\entities\BlogTag;
use yii\base\Model;
use yii\data\ActiveDataProvider;
class BlogTagSearch extends Model
{
public $id;
public $name;
public $slug;
public function rules(): array
{
return [
[['id'], 'integer'],
[['name', 'slug'], 'safe'],
];
}
/**
* @param array $params
* @return ActiveDataProvider
*/
public function search(array $params): ActiveDataProvider
{
$query = BlogTag::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => ['name' => SORT_ASC]
]
]);
$this->load($params);
if (!$this->validate()) {
$query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
]);
$query
->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'slug', $this->slug]);
return $dataProvider;
}
}

70
common/modules/blog/helpers/BlogPostHelper.php

@ -0,0 +1,70 @@
<?php
namespace common\modules\blog\helpers;
use core\entities\post\Post;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use Yii;
class BlogPostHelper
{
public static function statusList(): array
{
return [
Post::STATUS_DRAFT => Yii::t('blog', 'Draft'),
Post::STATUS_ACTIVE => Yii::t('blog', 'Active'),
];
}
public static function statusName($status): string
{
return ArrayHelper::getValue(self::statusList(), $status);
}
public static function statusLabel($status): string
{
switch ($status) {
case Post::STATUS_DRAFT:
$class = 'label label-default';
break;
case Post::STATUS_ACTIVE:
$class = 'label label-success';
break;
default:
$class = 'label label-default';
}
return Html::tag('span', ArrayHelper::getValue(self::statusList(), $status), [
'class' => $class,
]);
}
public static function parseYoutubeUrl($url)
{
$urls = parse_url($url);
//url is http://youtu.be/xxxx
if($urls['host'] == 'youtu.be'){
$id = ltrim($urls['path'],'/');
}
//url is http://www.youtube.com/embed/xxxx
else if(strpos($urls['path'],'embed') == 1){
$id = end(explode('/',$urls['path']));
}
//url is xxxx only
else if(strpos($url,'/')===false){
$id = $url;
}
//http://www.youtube.com/watch?feature=player_embedded&v=m-t4pcO99gI
//url is http://www.youtube.com/watch?v=xxxx
else{
parse_str($urls['query']);
/* @var $v */
$id = $v;
if(!empty($feature)){
$id = end(explode('v=',$urls['query']));
}
}
return $id;
}
}

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

@ -0,0 +1,61 @@
<?php
return [
'All Posts' => 'Все статьи',
'Posts' => 'Статьи',
'Categories' => 'Категории',
'Tags' => 'Теги',
'News' => 'Новости',
'Comments' => 'Комментарии',
'Articles' => 'Статьи',
'Blog' => 'Блог',
'Blogs' => 'Блоги',
'Events' => 'События',
'Gallery' => 'Фотогалерея',
'Create Post' => 'Новая статья',
'Create Category' => 'Новая категория',
'Create Tag' => 'Новый тег',
'Common' => 'Основное',
'Title' => 'Заголовок',
'Description' => 'Описание',
'Content' => 'Содержание',
'Image' => 'Изображение',
'Existing' => 'Существующие',
'New tag' => 'Новый тег',
'Category' => 'Категория',
'Post is already active.' => 'Статья уже опубликована.',
'Published At' => 'Опубликовано',
'Created At' => 'Создано',
'Updated At' => 'Обновлено',
'Status' => 'Статус',
'ID' => '№',
'SEO' => 'МЕТА теги',
'Tag Name' => 'Тег',
'SEO link' => 'ЧПУ ссылка',
'Draft' => 'Черновик',
'Active' => 'Опубликовано',
'Activate' => 'Опубликовать',
'Meta Title' => 'Заголовок',
'Meta Description' => 'Описание',
'Meta Keywords' => 'Ключевые слова',
'Name' => 'Название',
'Sort' => 'Позиция',
'Update: {name}' => 'Редактирование: {name}',
'Types' => 'Типы',
'Create Type' => 'Новый тип',
'Singular' => 'Единственное число',
'Plural' => 'Множественное число',
'Slug' => 'ЧПУ ссылка',
'Update Post: {name}' => 'Редактирование: {name}',
'Update Post Comment: {name}' => 'Редактирование комментария: {name}',
'Update Tag: {name}' => 'Редактирование тега: {name}',
'Parent Comment ID' => 'Родительский комментарий',
'Comment' => 'Комментарий',
'Published' => 'Опубликовано',
'User' => 'Пользователь',
'Post' => 'Статья',
'YouTube URL' => 'Ссылка YouTube',
'Add Image' => 'Новое изображение',
'Video' => 'Видео',
'If a video link is specified, the image will be used as a preview image' => 'Если указана ссылка на видео, то изображение будет рассматриваться как картинка для предварительного просмотра',
'Reset Image' => 'Сбросить изображение',
];

8
common/modules/blog/messages/ru/blog_public.php

@ -0,0 +1,8 @@
<?php
return [
'Blog' => 'Блог',
'Read More' => 'Подробнее',
'Leave a Comment' => 'Комментировать',
'Categories' => 'Категории',
];

31
common/modules/blog/migrations/m180605_184534_create_blog_categories_table.php

@ -0,0 +1,31 @@
<?php
use yii\db\Migration;
/**
* Handles the creation of table `blog_categories`.
*/
class m180605_184534_create_blog_categories_table extends Migration
{
public function up()
{
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
$this->createTable('{{%blog_categories}}', [
'id' => $this->primaryKey(),
'name' => $this->string()->notNull(),
'slug' => $this->string()->notNull(),
'title' => $this->string(),
'description' => $this->text(),
'sort' => $this->integer()->notNull(),
'meta_json' => 'LONGTEXT NOT NULL',
], $tableOptions);
$this->createIndex('{{%idx-blog_categories-slug}}', '{{%blog_categories}}', 'slug', true);
}
public function down()
{
$this->dropTable('{{%blog_categories}}');
}
}

42
common/modules/blog/migrations/m180605_190347_create_blog_posts_table.php

@ -0,0 +1,42 @@
<?php
use yii\db\Migration;
/**
* Handles the creation of table `blog_posts`.
*/
class m180605_190347_create_blog_posts_table extends Migration
{
public function up()
{
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
$this->createTable('{{%blog_posts}}', [
'id' => $this->primaryKey(),
'category_id' => $this->integer()->notNull(),
'published_at' => $this->integer()->unsigned()->notNull(),
'created_at' => $this->integer()->unsigned(),
'updated_at' => $this->integer()->unsigned(),
'title' => $this->string()->notNull(),
'description' => $this->text(),
'content' => 'MEDIUMTEXT',
'image' => $this->string(),
'video' => $this->string(),
'status' => $this->integer()->notNull(),
'meta_json' => 'TEXT NOT NULL',
'comments_count' => $this->integer()->notNull()->defaultValue(0),
'views' => $this->integer()->notNull()->defaultValue(0),
'slug' => $this->string()->notNull(),
], $tableOptions);
$this->createIndex('{{%idx-blog_posts-category_id}}', '{{%blog_posts}}', 'category_id');
$this->createIndex('{{%idx-blog_posts-slug}}', '{{%blog_posts}}', 'slug', true);
$this->addForeignKey('{{%fk-blog_posts-category_id}}', '{{%blog_posts}}', 'category_id', '{{%blog_categories}}', 'id');
}
public function down()
{
$this->dropForeignKey('{{%fk-blog_posts-category_id}}', '{{%blog_posts}}');
$this->dropTable('{{%blog_posts}}');
}
}

28
common/modules/blog/migrations/m180605_193423_create_blog_tags_table.php

@ -0,0 +1,28 @@
<?php
use yii\db\Migration;
/**
* Handles the creation of table `blog_tags`.
*/
class m180605_193423_create_blog_tags_table extends Migration
{
public function up()
{
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
$this->createTable('{{%blog_tags}}', [
'id' => $this->primaryKey(),
'name' => $this->string()->notNull(),
'slug' => $this->string()->notNull(),
], $tableOptions);
$this->createIndex('{{%idx-blog_tags-slug}}', '{{%blog_tags}}', 'slug', true);
}
public function down()
{
$this->dropIndex('{{%idx-blog_tags-slug}}', '{{%blog_tags}}');
$this->dropTable('{{%blog_tags}}');
}
}

36
common/modules/blog/migrations/m180605_193748_create_blog_tag_assignments_table.php

@ -0,0 +1,36 @@
<?php
use yii\db\Migration;
/**
* Handles the creation of table `blog_tag_assignments`.
*/
class m180605_193748_create_blog_tag_assignments_table extends Migration
{
public function up()
{
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
$this->createTable('{{%blog_tag_assignments}}', [
'post_id' => $this->integer()->notNull(),
'tag_id' => $this->integer()->notNull(),
], $tableOptions);
$this->addPrimaryKey('{{%pk-blog_tag_assignments}}', '{{%blog_tag_assignments}}', ['post_id', 'tag_id']);
$this->createIndex('{{%idx-blog_tag_assignments-post_id}}', '{{%blog_tag_assignments}}', 'post_id');
$this->createIndex('{{%idx-blog_tag_assignments-tag_id}}', '{{%blog_tag_assignments}}', 'tag_id');
$this->addForeignKey('{{%fk-blog_tag_assignments-post_id}}', '{{%blog_tag_assignments}}', 'post_id', '{{%blog_posts}}', 'id', 'CASCADE', 'RESTRICT');
$this->addForeignKey('{{%fk-blog_tag_assignments-tag_id}}', '{{%blog_tag_assignments}}', 'tag_id', '{{%blog_tags}}', 'id', 'CASCADE', 'RESTRICT');
}
public function down()
{
$this->dropIndex('{{%idx-blog_tag_assignments-tag_id}}', '{{%blog_tag_assignments}}');
$this->dropIndex('{{%idx-blog_tag_assignments-post_id}}', '{{%blog_tag_assignments}}');
$this->dropForeignKey('{{%fk-blog_tag_assignments-tag_id}}', '{{%blog_tag_assignments}}');
$this->dropForeignKey('{{%fk-blog_tag_assignments-post_id}}', '{{%blog_tag_assignments}}');
$this->dropTable('{{%blog_tag_assignments}}');
}
}

43
common/modules/blog/migrations/m180605_194207_create_blog_comments_table.php

@ -0,0 +1,43 @@
<?php
use yii\db\Migration;
/**
* Handles the creation of table `blog_comments`.
*/
class m180605_194207_create_blog_comments_table extends Migration
{
public function up()
{
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
$this->createTable('{{%blog_comments}}', [
'id' => $this->primaryKey(),
'post_id' => $this->integer()->notNull(),
'user_id' => $this->integer()->notNull(),
'parent_id' => $this->integer(),
'created_at' => $this->integer()->unsigned()->notNull(),
'text' => $this->text()->notNull(),
'active' => $this->boolean()->notNull(),
], $tableOptions);
$this->createIndex('{{%idx-blog_comments-post_id}}', '{{%blog_comments}}', 'post_id');
$this->createIndex('{{%idx-blog_comments-user_id}}', '{{%blog_comments}}', 'user_id');
$this->createIndex('{{%idx-blog_comments-parent_id}}', '{{%blog_comments}}', 'parent_id');
$this->addForeignKey('{{%fk-blog_comments-post_id}}', '{{%blog_comments}}', 'post_id', '{{%blog_posts}}', 'id', 'CASCADE');
$this->addForeignKey('{{%fk-blog_comments-user_id}}', '{{%blog_comments}}', 'user_id', '{{%users}}', 'id', 'CASCADE');
$this->addForeignKey('{{%fk-blog_comments-parent_id}}', '{{%blog_comments}}', 'parent_id', '{{%blog_comments}}', 'id', 'CASCADE');
}
public function down()
{
$this->dropForeignKey('{{%fk-blog_comments-parent_id}}', '{{%blog_comments}}');
$this->dropForeignKey('{{%fk-blog_comments-user_id}}', '{{%blog_comments}}');
$this->dropForeignKey('{{%fk-blog_comments-post_id}}', '{{%blog_comments}}');
$this->dropIndex('{{%idx-blog_comments-parent_id}}', '{{%blog_comments}}');
$this->dropIndex('{{%idx-blog_comments-user_id}}', '{{%blog_comments}}');
$this->dropIndex('{{%idx-blog_comments-post_id}}', '{{%blog_comments}}');
$this->dropTable('{{%blog_comments}}');
}
}

31
common/modules/blog/repositories/BlogCategoryRepository.php

@ -0,0 +1,31 @@
<?php
namespace common\modules\blog\repositories;
use common\modules\blog\entities\BlogCategory;
use core\repositories\NotFoundException;
class BlogCategoryRepository
{
public function get($id): BlogCategory
{
if (!$category = BlogCategory::findOne($id)) {
throw new NotFoundException('Category is not found.');
}
return $category;
}
public function save(BlogCategory $category): void
{
if (!$category->save()) {
throw new \RuntimeException('Saving error.');
}
}
public function remove(BlogCategory $category): void
{
if (!$category->delete()) {
throw new \RuntimeException('Removing error.');
}
}
}

36
common/modules/blog/repositories/BlogRepository.php

@ -0,0 +1,36 @@
<?php
namespace common\modules\blog\repositories;
use common\modules\blog\entities\BlogPost;
use core\repositories\NotFoundException;
class BlogRepository
{
public function get($id): BlogPost
{
if (!$post = BlogPost::findOne($id)) {
throw new NotFoundException('Post is not found.');
}
return $post;
}
public function existsByCategory($id): bool
{
return BlogPost::find()->andWhere(['category_id' => $id])->exists();
}
public function save(BlogPost $post): void
{
if (!$post->save()) {
throw new \RuntimeException('Saving error.');
}
}
public function remove(BlogPost $post): void
{
if (!$post->delete()) {
throw new \RuntimeException('Removing error.');
}
}
}

36
common/modules/blog/repositories/BlogTagRepository.php

@ -0,0 +1,36 @@
<?php
namespace common\modules\blog\repositories;
use common\modules\blog\entities\BlogTag;
use core\repositories\NotFoundException;
class BlogTagRepository
{
public function get($id): BlogTag
{
if (!$tag = BlogTag::findOne($id)) {
throw new NotFoundException('Tag is not found.');
}
return $tag;
}
public function findByName($name): ?BlogTag
{
return BlogTag::findOne(['name' => $name]);
}
public function save(BlogTag $tag): void
{
if (!$tag->save()) {
throw new \RuntimeException('Saving error.');
}
}
public function remove(BlogTag $tag): void
{
if (!$tag->delete()) {
throw new \RuntimeException('Removing error.');
}
}
}

23
common/modules/blog/repositories/read/BlogCategoryReadRepository.php

@ -0,0 +1,23 @@
<?php
namespace common\modules\blog\repositories\read;
use common\modules\blog\entities\BlogCategory;
class BlogCategoryReadRepository
{
public function getAll(): array
{
return BlogCategory::find()->orderBy('sort')->all();
}
public function find($id): ?BlogCategory
{
return BlogCategory::findOne($id);
}
public function findBySlug($slug): ?BlogCategory
{
return BlogCategory::find()->andWhere(['slug' => $slug])->one();
}
}

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

@ -0,0 +1,94 @@
<?php
namespace common\modules\blog\repositories\read;
use common\modules\blog\entities\BlogCategory;
use common\modules\blog\entities\BlogPost;
use common\modules\blog\entities\BlogTag;
use yii\data\ActiveDataProvider;
use yii\data\DataProviderInterface;
use yii\db\ActiveQuery;
class BlogPostReadRepository
{
public function count(): int
{
return BlogPost::find()->active()->count();
}
public function getAllByRange($offset, $limit): array
{
return BlogPost::find()->active()->orderBy(['id' => SORT_ASC])->limit($limit)->offset($offset)->all();
}
public function getAll(): DataProviderInterface
{
$query = BlogPost::find()->active()->with('category');
return $this->getProvider($query);
}
public function getAllByCategory(BlogCategory $category): DataProviderInterface
{
$query = BlogPost::find()->active()->andWhere(['category_id' => $category->id])->with('category');
return $this->getProvider($query);
}
public function findNext(int $id): ?BlogPost
{
return BlogPost::find()->active()->andWhere(['>', 'id', $id])->one();
}
public function findPrev(int $id): ?BlogPost
{
return BlogPost::find()->active()->andWhere(['<', 'id', $id])->orderBy(['id' => SORT_DESC])->one();
}
public function getAllByTag(BlogTag $tag): DataProviderInterface
{
$query = BlogPost::find()->alias('p')->active('p')->with('category');
$query->joinWith(['blogTagAssignments ta'], false);
$query->andWhere(['ta.tag_id' => $tag->id]);
$query->groupBy('p.id');
return $this->getProvider($query);
}
public function getByTagsId(BlogPost $post, array $tag_ids, int $limit = 15): DataProviderInterface
{
$query = BlogPost::find()->alias('p')->active('p')->with('category');
$query->joinWith(['blogTagAssignments ta'], false);
$query->andWhere(['ta.tag_id' => $tag_ids]);
$query->andWhere(['!=', 'p.id', $post->id]);
$query->groupBy('p.id');
$query->limit($limit);
return $this->getProvider($query);
}
public function getLast($limit): array
{
return BlogPost::find()->with('category')->orderBy(['id' => SORT_DESC])->limit($limit)->all();
}
public function getPopular($limit): array
{
return BlogPost::find()->with('category')->orderBy(['comments_count' => SORT_DESC])->limit($limit)->all();
}
public function find($id): ?BlogPost
{
return BlogPost::find()->active()->andWhere(['id' => $id])->one();
}
private function getProvider(ActiveQuery $query): ActiveDataProvider
{
return new ActiveDataProvider([
'query' => $query,
'sort' => ['defaultOrder' => ['created_at' => SORT_DESC]],
]);
}
public function findBySlug($slug): ?BlogPost
{
return BlogPost::find()->andWhere(['slug' => $slug])->one();
}
}

18
common/modules/blog/repositories/read/BlogTagReadRepository.php

@ -0,0 +1,18 @@
<?php
namespace common\modules\blog\repositories\read;
use common\modules\blog\entities\BlogTag;
class BlogTagReadRepository
{
public function find($id): ?BlogTag
{
return BlogTag::findOne($id);
}
public function findBySlug($slug): ?BlogTag
{
return BlogTag::find()->andWhere(['slug' => $slug])->one();
}
}

66
common/modules/blog/services/BlogCategoryManageService.php

@ -0,0 +1,66 @@
<?php
namespace common\modules\blog\services;
use common\modules\blog\entities\BlogCategory;
use common\modules\blog\forms\BlogCategoryForm;
use common\modules\blog\repositories\BlogCategoryRepository;
use common\modules\blog\repositories\BlogRepository;
use core\entities\Meta;
class BlogCategoryManageService
{
private $categories;
private $posts;
public function __construct(BlogCategoryRepository $categories, BlogRepository $posts)
{
$this->categories = $categories;
$this->posts = $posts;
}
public function create(BlogCategoryForm $form): BlogCategory
{
$category = BlogCategory::create(
$form->name,
$form->slug,
$form->title,
$form->description,
$form->sort,
new Meta(
$form->meta->title,
$form->meta->description,
$form->meta->keywords
)
);
$this->categories->save($category);
return $category;
}
public function edit($id, BlogCategoryForm $form): void
{
$category = $this->categories->get($id);
$category->edit(
$form->name,
$form->slug,
$form->title,
$form->description,
$form->sort,
new Meta(
$form->meta->title,
$form->meta->description,
$form->meta->keywords
)
);
$this->categories->save($category);
}
public function remove($id): void
{
$category = $this->categories->get($id);
if ($this->posts->existsByCategory($category->id)) {
throw new \DomainException('Unable to remove category with posts.');
}
$this->categories->remove($category);
}
}

37
common/modules/blog/services/BlogCommentManageService.php

@ -0,0 +1,37 @@
<?php
namespace common\modules\blog\services;
use common\modules\blog\forms\BlogCommentEditForm;
use common\modules\blog\repositories\BlogRepository;
class BlogCommentManageService
{
private $posts;
public function __construct(BlogRepository $posts)
{
$this->posts = $posts;
}
public function edit($postId, $id, BlogCommentEditForm $form): void
{
$post = $this->posts->get($postId);
$post->editComment($id, $form->parentId, $form->text);
$this->posts->save($post);
}
public function activate($postId, $id): void
{
$post = $this->posts->get($postId);
$post->activateComment($id);
$this->posts->save($post);
}
public function remove($postId, $id): void
{
$post = $this->posts->get($postId);
$post->removeComment($id);
$this->posts->save($post);
}
}

32
common/modules/blog/services/BlogCommentService.php

@ -0,0 +1,32 @@
<?php
namespace common\modules\blog\services;
use common\modules\blog\entities\BlogComment;
use common\modules\blog\forms\BlogCommentForm;
use common\modules\blog\repositories\BlogRepository;
use core\repositories\user\UserRepository;
class BlogCommentService
{
private $posts;
private $users;
public function __construct(BlogRepository $posts, UserRepository $users)
{
$this->posts = $posts;
$this->users = $users;
}
public function create($postId, $userId, BlogCommentForm $form): BlogComment
{
$post = $this->posts->get($postId);
$user = $this->users->get($userId);
$comment = $post->addComment($user->id, $form->parentId, $form->text);
$this->posts->save($post);
return $comment;
}
}

162
common/modules/blog/services/BlogPostManageService.php

@ -0,0 +1,162 @@
<?php
namespace common\modules\blog\services;
use common\modules\blog\entities\BlogPost;
use common\modules\blog\entities\BlogTag;
use common\modules\blog\forms\BlogPostForm;
use common\modules\blog\helpers\BlogPostHelper;
use common\modules\blog\repositories\BlogCategoryRepository;
use common\modules\blog\repositories\BlogRepository;
use common\modules\blog\repositories\BlogTagRepository;
use core\entities\Meta;
use core\services\TransactionManager;
use yii\base\Security;
use yii\helpers\Inflector;
class BlogPostManageService
{
private $posts;
private $categories;
private $tags;
private $transaction;
public function __construct(
BlogRepository $posts,
BlogCategoryRepository $categories,
BlogTagRepository $tags,
TransactionManager $transaction
)
{
$this->posts = $posts;
$this->categories = $categories;
$this->tags = $tags;
$this->transaction = $transaction;
}
public function create(BlogPostForm $form): BlogPost
{
$category = $this->categories->get($form->category_id);
$post = BlogPost::create(
$category->id,
$form->title,
$form->slug,
$form->description,
$form->content,
$form->published_at,
$form->video,
new Meta(
$form->meta->title,
$form->meta->description,
$form->meta->keywords
)
);
if ($form->image) {
$post->setImage($form->image);
}
else if ($form->video) {
$src = 'https://i.ytimg.com/vi/' . BlogPostHelper::parseYoutubeUrl($post->video) . '/maxresdefault.jpg';
$filename = (new Security())->generateRandomString(15) . '.jpg';
copy($src, \Yii::getAlias(BlogPost::FILE_ORIGINAL_PATH . '/' . $post->id . '.jpg'));
$post->image = $filename;
}
$this->transaction->wrap(function () use ($post, $form) {
if (is_array($form->tags->new_tags) && !empty($form->tags->new_tags)) {
foreach ( $form->tags->new_tags as $tag_id => $tag_name ) {
if ( !$tag = $this->tags->findByName( $tag_name ) ) {
$tag = BlogTag::create( $tag_name, Inflector::slug( $tag_name, '_' ) );
$this->tags->save( $tag );
}
$post->assignTag( $tag->id );
}
}
$this->posts->save($post);
});
return $post;
}
public function edit($id, BlogPostForm $form): void
{
$post = $this->posts->get($id);
$category = $this->categories->get($form->category_id);
$post->edit(
$category->id,
$form->title,
$form->slug,
$form->description,
$form->content,
$form->published_at,
$form->video,
new Meta(
$form->meta->title,
$form->meta->description,
$form->meta->keywords
)
);
if ($form->image) {
$post->cleanFiles();
$post->setImage($form->image);
}
elseif ($form->video && (!$post->image || $form->reset_image)) {
$post->cleanFiles();
$src = 'https://i.ytimg.com/vi/' . BlogPostHelper::parseYoutubeUrl($post->video) . '/maxresdefault.jpg';
$filename = (new Security())->generateRandomString(15) . '.jpg';
copy($src, \Yii::getAlias(BlogPost::FILE_ORIGINAL_PATH . '/' . $post->id . '.jpg'));
$post->image = $filename;
}
elseif ($post->image && $form->reset_image) {
$post->cleanFiles();
//Post::updateAll(['image' => null], ['id' => $post->id]);
$post->image = null;
$post->updateAttributes(['image']);
}
$this->transaction->wrap(function () use ($post, $form) {
$post->revokeTags();
$this->posts->save($post);
$tag_updated = false;
if (is_array($form->tags->new_tags) && !empty($form->tags->new_tags)) {
foreach ( $form->tags->new_tags as $tag_id => $tag_name ) {
if ( ! $tag = $this->tags->findByName( $tag_name ) ) {
$tag = BlogTag::create( $tag_name, Inflector::slug( $tag_name, '_' ) );
$this->tags->save( $tag );
}
$post->assignTag( $tag->id );
$tag_updated = true;
}
}
if ($tag_updated) {
$this->posts->save( $post );
}
});
}
public function activate($id): void
{
$post = $this->posts->get($id);
$post->activate();
$this->posts->save($post);
}
public function draft($id): void
{
$post = $this->posts->get($id);
$post->draft();
$this->posts->save($post);
}
public function remove($id): void
{
$post = $this->posts->get($id);
$this->posts->remove($post);
}
}

43
common/modules/blog/services/BlogTagManageService.php

@ -0,0 +1,43 @@
<?php
namespace common\modules\blog\services;
use common\modules\blog\entities\BlogTag;
use common\modules\blog\forms\BlogTagSingleForm;
use common\modules\blog\repositories\BlogTagRepository;
class BlogTagManageService
{
private $tags;
public function __construct(BlogTagRepository $tags)
{
$this->tags = $tags;
}
public function create(BlogTagSingleForm $form): BlogTag
{
$tag = BlogTag::create(
$form->name,
$form->slug
);
$this->tags->save($tag);
return $tag;
}
public function edit($id, BlogTagSingleForm $form): void
{
$tag = $this->tags->get($id);
$tag->edit(
$form->name,
$form->slug
);
$this->tags->save($tag);
}
public function remove($id): void
{
$tag = $this->tags->get($id);
$this->tags->remove($tag);
}
}

91
common/modules/blog/urls/BlogCategoryUrlRule.php

@ -0,0 +1,91 @@
<?php
namespace common\modules\blog\urls;
use yii\caching\TagDependency;
use common\modules\blog\repositories\read\BlogCategoryReadRepository;
use yii\base\BaseObject;
use yii\base\InvalidParamException;
use yii\caching\Cache;
use yii\web\UrlNormalizerRedirectException;
use yii\web\UrlRuleInterface;
class BlogCategoryUrlRule extends BaseObject implements UrlRuleInterface
{
public $prefix = 'blog/category';
private $repository;
private $cache;
public function __construct(BlogCategoryReadRepository $repository, Cache $cache, $config = [])
{
parent::__construct($config);
$this->repository = $repository;
$this->cache = $cache;
}
public function parseRequest($manager, $request)
{
if (preg_match('#^' . $this->prefix . '/(.*[a-z])$#is', $request->pathInfo, $matches)) {
$path = $matches['1'];
$result = $this->cache->getOrSet( [ 'blog_category_route', 'path' => $path ], function () use ( $path ) {
if ( ! $post = $this->repository->findBySlug( $this->getPathSlug( $path ) ) ) {
return [ 'id' => null, 'path' => null ];
}
return [ 'id' => $post->id, 'path' => $post->slug ];
}, null, new TagDependency(['tags' => ['blog']]) );
if ( empty( $result['id'] ) ) {
return false;
}
if ( $path != $result['path'] ) {
throw new UrlNormalizerRedirectException( [
'blog/post/category',
'id' => $result['id'],
], 301 );
}
return [ 'blog/post/category', [ 'id' => $result['id'] ] ];
}
return false;
}
public function createUrl($manager, $route, $params)
{
if ($route == 'blog/post/category') {
if (empty($params['id'])) {
throw new InvalidParamException('Empty id.');
}
$id = $params['id'];
$url = $this->cache->getOrSet(['blog_category_route', 'id' => $id], function () use ($id) {
if (!$post = $this->repository->find($id)) {
return null;
}
return $post->slug;
//return $this->getPagePath($post);
}, null, new TagDependency(['tags' => ['blog']]));
if (!$url) {
throw new InvalidParamException('Undefined id.');
}
$url = $this->prefix . '/' . $url;
unset($params['id']);
if (!empty($params) && ($query = http_build_query($params)) !== '') {
$url .= '?' . $query;
}
return $url;
}
return false;
}
private function getPathSlug($path): string
{
$chunks = explode('/', $path);
return end($chunks);
}
}

88
common/modules/blog/urls/BlogMainUrlRule.php

@ -0,0 +1,88 @@
<?php
namespace common\modules\blog\urls;
use common\modules\blog\repositories\read\BlogPostReadRepository;
use yii\base\BaseObject;
use yii\base\InvalidParamException;
use yii\caching\Cache;
use yii\caching\TagDependency;
use yii\web\UrlNormalizerRedirectException;
use yii\web\UrlRuleInterface;
use InvalidArgumentException;
class BlogMainUrlRule extends BaseObject implements UrlRuleInterface
{
public $prefix = 'blog/post';
private $repository;
private $cache;
public function __construct(BlogPostReadRepository $repository, Cache $cache, $config = [])
{
parent::__construct($config);
$this->repository = $repository;
$this->cache = $cache;
}
public function parseRequest($manager, $request)
{
if (preg_match('#^' . $this->prefix . '/(.*[a-z])$#is', $request->pathInfo, $matches)) {
$path = $matches['1'];
$result = $this->cache->getOrSet( [ 'blog_main_route', 'path' => $path ], function () use ( $path ) {
if ( ! $post = $this->repository->findBySlug($this->getPathSlug( $path ) ) ) {
return [ 'id' => null, 'path' => null ];
}
return [ 'id' => $post->id, 'path' => $post->slug ];
}, null, new TagDependency(['tags' => ['blog']]) );
if ( empty( $result['id'] ) ) {
return false;
}
if ( $path != $result['path'] ) {
throw new UrlNormalizerRedirectException( [ 'blog/post/post', 'id' => $result['id'] ], 301 );
}
return ['blog/post/post', ['id' => $result['id']]];
}
return false;
}
public function createUrl($manager, $route, $params)
{
if ($route == 'blog/post/post') {
if (empty($params['id'])) {
throw new InvalidArgumentException('Empty id.');
}
$id = $params['id'];
$url = $this->cache->getOrSet(['post_main_route', 'id' => $id], function () use ($id) {
if (!$post = $this->repository->find($id)) {
return null;
}
return $post->slug;
}, null, new TagDependency(['tags' => ['blog']]));
if (!$url) {
throw new InvalidParamException('Undefined id.');
}
$url = $this->prefix . '/' . $url;
unset($params['id']);
if (!empty($params) && ($query = http_build_query($params)) !== '') {
$url .= '?' . $query;
}
return $url;
}
return false;
}
private function getPathSlug($path): string
{
$chunks = explode('/', $path);
return end($chunks);
}
}

91
common/modules/blog/urls/BlogTagUrlRule.php

@ -0,0 +1,91 @@
<?php
namespace common\modules\blog\urls;
use yii\caching\TagDependency;
use common\modules\blog\repositories\read\BlogTagReadRepository;
use yii\base\BaseObject;
use yii\base\InvalidParamException;
use yii\caching\Cache;
use yii\web\UrlNormalizerRedirectException;
use yii\web\UrlRuleInterface;
class BlogTagUrlRule extends BaseObject implements UrlRuleInterface
{
public $prefix = 'blog/tag';
private $repository;
private $cache;
public function __construct(BlogTagReadRepository $repository, Cache $cache, $config = [])
{
parent::__construct($config);
$this->repository = $repository;
$this->cache = $cache;
}
public function parseRequest($manager, $request)
{
if (preg_match('#^' . $this->prefix . '/(.*[a-z])$#is', $request->pathInfo, $matches)) {
$path = $matches['1'];
$result = $this->cache->getOrSet( [ 'blog_tag_route', 'path' => $path ], function () use ( $path ) {
if ( ! $post = $this->repository->findBySlug( $this->getPathSlug( $path ) ) ) {
return [ 'id' => null, 'path' => null ];
}
return [ 'id' => $post->id, 'path' => $post->slug ];
}, null, new TagDependency(['tags' => ['blog']]) );
if ( empty( $result['id'] ) ) {
return false;
}
if ( $path != $result['path'] ) {
throw new UrlNormalizerRedirectException( [
'blog/post/tag',
'id' => $result['id'],
], 301 );
}
return [ 'blog/post/tag', [ 'id' => $result['id'] ] ];
}
return false;
}
public function createUrl($manager, $route, $params)
{
if ($route == 'blog/post/tag') {
if (empty($params['id'])) {
throw new InvalidParamException('Empty id.');
}
$id = $params['id'];
$url = $this->cache->getOrSet(['blog_tag_route', 'id' => $id], function () use ($id) {
if (!$post = $this->repository->find($id)) {
return null;
}
return $post->slug;
//return $this->getPagePath($post);
}, null, new TagDependency(['tags' => ['blog']]));
if (!$url) {
throw new InvalidParamException('Undefined id.');
}
$url = $this->prefix . '/' . $url;
unset($params['id']);
if (!empty($params) && ($query = http_build_query($params)) !== '') {
$url .= '?' . $query;
}
return $url;
}
return false;
}
private function getPathSlug($path): string
{
$chunks = explode('/', $path);
return end($chunks);
}
}

54
common/modules/blog/views/manage/category/_form.php

@ -0,0 +1,54 @@
<?php
use mihaildev\ckeditor\CKEditor;
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $model \common\modules\blog\forms\BlogCategoryForm */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="category-form">
<?php $form = ActiveForm::begin(); ?>
<div class="box box-default">
<div class="box-header with-border"><?= Yii::t('blog', 'Common') ?></div>
<div class="box-body">
<div class="row">
<div class="col-md-2">
<?= $form->field($model, 'sort')->textInput(['maxlength' => true]) ?>
</div>
<div class="col-md-5">
<?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>
</div>
<div class="col-md-5">
<?= $form->field($model, 'slug')->textInput(['maxlength' => true]) ?>
</div>
</div>
<?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'description')->widget(CKEditor::className()) ?>
</div>
</div>
<div class="box box-default">
<div class="box-header with-border"><?= Yii::t('blog', 'SEO') ?></div>
<div class="box-body">
<?= $form->field($model->meta, 'title')->textInput() ?>
<?= $form->field($model->meta, 'description')->textarea(['rows' => 2]) ?>
<?= $form->field($model->meta, 'keywords')->textInput() ?>
</div>
</div>
<div class="form-group">
<?= Html::submitButton(Yii::t('buttons','Save'), ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

17
common/modules/blog/views/manage/category/create.php

@ -0,0 +1,17 @@
<?php
/* @var $this yii\web\View */
/* @var $model \common\modules\blog\forms\BlogCategoryForm */
$title = Yii::t('blog', 'Create Category');
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Categories'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $title;
?>
<div class="category-create">
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

54
common/modules/blog/views/manage/category/index.php

@ -0,0 +1,54 @@
<?php
use common\modules\blog\entities\BlogCategory;
use yii\grid\ActionColumn;
use yii\helpers\Html;
use yii\grid\GridView;
/* @var $this yii\web\View */
/* @var $searchModel \common\modules\blog\forms\search\BlogCategorySearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$title = Yii::t('blog', 'Categories');
$this->title = $title;
$this->params['breadcrumbs'][] = $title;
?>
<div class="user-index">
<p>
<?= Html::a(Yii::t('blog', 'Create Category'), ['create'], ['class' => 'btn btn-success']) ?>
</p>
<div class="box">
<div class="box-body">
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
[
'attribute' => 'sort',
'options' => ['style' => 'width: 100px;'],
],
[
'attribute' => 'name',
'value' => function (BlogCategory $model) {
return Html::a(Html::encode($model->name), ['view', 'id' => $model->id]);
},
'format' => 'raw',
],
[
'attribute' => 'slug',
],
[
'attribute' => 'title',
],
[
'class' => ActionColumn::class,
'options' => ['style' => 'width: 100px;'],
'contentOptions' => ['class' => 'text-center'],
],
],
]); ?>
</div>
</div>
</div>

19
common/modules/blog/views/manage/category/update.php

@ -0,0 +1,19 @@
<?php
/* @var $this yii\web\View */
/* @var $category \common\modules\blog\entities\BlogCategory */
/* @var $model \common\modules\blog\forms\BlogCategoryForm */
$title = Yii::t('blog', 'Update: {name}', ['name' => $category->name]);
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Categories'), 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $category->name, 'url' => ['view', 'id' => $category->id]];
$this->params['breadcrumbs'][] = Yii::t('buttons', 'Editing');
?>
<div class="category-update">
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

70
common/modules/blog/views/manage/category/view.php

@ -0,0 +1,70 @@
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
/* @var $this yii\web\View */
/* @var $category \common\modules\blog\entities\BlogCategory */
$title = $category->name;
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Categories'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $title;
?>
<div class="user-view">
<p>
<?= Html::a(Yii::t('blog', 'Categories'), ['index'], ['class' => 'btn btn-default']) ?>
<?= Html::a(Yii::t('buttons', 'Edit'), ['update', 'id' => $category->id], ['class' => 'btn btn-primary']) ?>
<?= Html::a(Yii::t('buttons', 'Delete'), ['delete', 'id' => $category->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => Yii::t('buttons', 'Are you sure you want to delete this item?'),
'method' => 'post',
],
]) ?>
</p>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'Common') ?></div>
<div class="box-body">
<?= DetailView::widget([
'model' => $category,
'attributes' => [
'id',
'name',
'slug',
'title',
'sort',
],
]) ?>
</div>
</div>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'Description') ?></div>
<div class="box-body">
<?= Yii::$app->formatter->asHtml($category->description, [
'Attr.AllowedRel' => array('nofollow'),
'HTML.SafeObject' => true,
'Output.FlashCompat' => true,
'HTML.SafeIframe' => true,
'URI.SafeIframeRegexp'=>'%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%',
]) ?>
</div>
</div>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'SEO') ?></div>
<div class="box-body">
<?= DetailView::widget([
'model' => $category,
'attributes' => [
'meta.title',
'meta.description',
'meta.keywords',
],
]) ?>
</div>
</div>
</div>

54
common/modules/blog/views/manage/comment/index.php

@ -0,0 +1,54 @@
<?php
use common\modules\blog\entities\BlogComment;
use yii\grid\ActionColumn;
use yii\grid\GridView;
use yii\helpers\StringHelper;
/* @var $this yii\web\View */
/* @var $searchModel \common\modules\blog\forms\search\BlogCommentSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = Yii::t('blog', 'Comments');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="blog-comments-index">
<div class="box">
<div class="box-body">
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
[
'attribute' => 'id',
'options' => ['style' => 'width: 40px;'],
'contentOptions' => ['class' => 'text-center'],
],
[
'attribute' => 'text',
'value' => function (BlogComment $model) {
return StringHelper::truncate(strip_tags($model->text), 100);
},
],
[
'attribute' => 'created_at',
'format' => ['datetime', 'php:d.m.Y H:i'],
'options' => ['style' => 'width: 60px;'],
'contentOptions' => ['class' => 'text-center'],
],
[
'attribute' => 'active',
'filter' => $searchModel->activeList(),
'format' => 'boolean',
'contentOptions' => ['style' => 'width: 150px'],
],
[
'class' => ActionColumn::class,
'options' => ['style' => 'width: 100px;'],
'contentOptions' => ['class' => 'text-center'],
],
],
]); ?>
</div>
</div>
</div>

34
common/modules/blog/views/manage/comment/update.php

@ -0,0 +1,34 @@
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
/* @var $this yii\web\View */
/* @var $post \common\modules\blog\entities\BlogPost */
/* @var $model \common\modules\blog\forms\BlogCommentEditForm */
$this->title = Yii::t('blog', 'Update Post Comment: {name}', ['name' => $post->title]);
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Comment'), 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $post->title, 'url' => ['view', 'id' => $post->id]];
$this->params['breadcrumbs'][] = Yii::t('buttons', 'Editing');
?>
<div class="post-update">
<?php $form = ActiveForm::begin([
'options' => ['enctype'=>'multipart/form-data']
]); ?>
<div class="box box-default">
<div class="box-header with-border"><?= Yii::t('blog', 'Common') ?></div>
<div class="box-body">
<?= $form->field($model, 'parentId')->textInput() ?>
<?= $form->field($model, 'text')->textarea(['rows' => 20]) ?>
</div>
</div>
<div class="form-group">
<?= Html::submitButton(Yii::t('buttons', 'Save'), ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

71
common/modules/blog/views/manage/comment/view.php

@ -0,0 +1,71 @@
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
use core\entities\user\User;
use common\modules\blog\entities\BlogComment;
/* @var $this yii\web\View */
/* @var $post \common\modules\blog\entities\BlogPost */
/* @var $comment BlogComment */
/* @var $modificationsProvider yii\data\ActiveDataProvider */
$this->title = $post->title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Comments'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="user-view">
<p>
<?= Html::a(Yii::t('blog', 'Comments'), ['index'], ['class' => 'btn btn-default']) ?>
<?= Html::a(Yii::t('buttons', 'Edit'), ['update', 'post_id' => $post->id, 'id' => $comment->id], ['class' => 'btn btn-primary']) ?>
<?php if ($comment->isActive()): ?>
<?= Html::a(Yii::t('buttons', 'Delete'), ['delete', 'post_id' => $post->id, 'id' => $comment->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => Yii::t('buttons', 'Are you sure you want to delete this item?'),
'method' => 'post',
],
]) ?>
<?php else: ?>
<?= Html::a(Yii::t('buttons', 'Restore'), ['activate', 'post_id' => $post->id, 'id' => $comment->id], [
'class' => 'btn btn-success',
'data' => [
'confirm' => Yii::t('buttons', 'Are you sure you want to activate this item?'),
'method' => 'post',
],
]) ?>
<?php endif; ?>
</p>
<div class="box">
<div class="box-body">
<?= DetailView::widget([
'model' => $comment,
'attributes' => [
'id',
'created_at:boolean',
'active:boolean',
[
'attribute' => 'user_id',
'value' => function(BlogComment $comment) {
return User::findOne($comment->user_id)->username;
},
],
'parent_id',
[
'attribute' => 'post_id',
'value' => $post->title,
],
],
]) ?>
</div>
</div>
<div class="box">
<div class="box-body">
<?= Yii::$app->formatter->asNtext($comment->text) ?>
</div>
</div>
</div>

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

@ -0,0 +1,144 @@
<?php
use kartik\file\FileInput;
use mihaildev\ckeditor\CKEditor;
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\web\JsExpression;
use yii\helpers\Url;
use yii\helpers\Json;
use yii\helpers\ArrayHelper;
use kartik\widgets\DateTimePicker;
use kartik\widgets\Select2;
/* @var $this yii\web\View */
/* @var $model \common\modules\blog\forms\BlogPostForm */
/* @var $form yii\widgets\ActiveForm */
$fetchUrl = Url::to( [ 'tag-search' ] );
if (isset($model->_post)) {
$tagsJson = Json::encode( Json::encode( ArrayHelper::map( $model->_post->tags, 'id', 'name' ) ) );
$js = <<<JS
var arr = {$tagsJson};
$.each(JSON.parse(arr), function( key, value ) {
$("#posttagform-new_tags").append("<option value='"+value+"' selected>"+value+"</option>");
});
$('#posttagform-new_tags').trigger('change');
JS;
$this->registerJs( $js, $this::POS_READY );
}
?>
<div class="post-form">
<?php $form = ActiveForm::begin([
'options' => ['enctype'=>'multipart/form-data']
]); ?>
<div class="row">
<div class="col-md-6">
<div class="box box-default">
<div class="box-header with-border"><?= Yii::t('blog', 'Common') ?></div>
<div class="box-body">
<?= $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 class="col-md-6">
<div class="box box-default">
<div class="box-header with-border"><?= Yii::t('blog', 'Tags') ?></div>
<div class="box-body">
<?= $form->field($model->tags, 'new_tags')->widget(Select2::classname(), [
'options' => [
'placeholder' => Yii::t('blog','Set tags...'),
'multiple' => true,
],
'pluginOptions' => [
'tags' => true,
'tokenSeparators' => [',', ' '],
'maximumInputLength' => 12,
'ajax' => [
'url' => $fetchUrl,
'dataType' => 'json',
'data' => new JsExpression('function(params) { return {q:params.term}; }')
],
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('function(tag) { return tag.text; }'),
'templateSelection' => new JsExpression('function (tag) { return tag.text; }'),
],
])->label(false); ?>
</div>
</div>
</div>
</div>
<div class="box box-default">
<div class="box-body">
<?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'slug')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'description')->textarea(['rows' => 5]) ?>
<?= $form->field($model, 'content')->widget(CKEditor::className()) ?>
</div>
</div>
<div class="box box-default">
<div class="box-body">
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#field_image"><?= Yii::t('blog', 'Image') ?></a></li>
<li><a data-toggle="tab" href="#field_video"><?= Yii::t('blog', 'Video') ?></a></li>
</ul>
<div class="tab-content">
<div id="field_image" class="tab-pane fade in active" style="padding-top: 20px;">
<?= $form->field($model, 'image')->label(false)->widget(FileInput::class, [
'options' => [
'accept' => 'image/*',
],
'pluginOptions' => [
'showUpload' => false,
],
]) ?>
<?php if (isset($model->_post) && $model->_post->image): ?>
<?= Html::img($model->_post->getThumbFileUrl('image', 'thumb_gallery_view'), [
'class' => 'thumbnail',
'width' => 300,
]) ?>
<?= $form->field($model, 'reset_image')->checkbox() ?>
<?php endif; ?>
</div>
<div id="field_video" class="tab-pane fade" style="padding-top: 20px;">
<?= $form->field($model, 'video')->textInput()->label(Yii::t('blog', 'YouTube URL'))->hint(Yii::t('blog', 'If a video link is specified, the image will be used as a preview image')) ?>
</div>
</div>
</div>
</div>
<div class="box box-default">
<div class="box-header with-border"><?= Yii::t('blog', 'SEO') ?></div>
<div class="box-body">
<?= $form->field($model->meta, 'title')->textInput() ?>
<?= $form->field($model->meta, 'description')->textarea(['rows' => 2]) ?>
<?= $form->field($model->meta, 'keywords')->textInput() ?>
</div>
</div>
<div class="form-group">
<?= Html::submitButton(Yii::t('buttons', 'Save'), ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

17
common/modules/blog/views/manage/post/create.php

@ -0,0 +1,17 @@
<?php
/* @var $this yii\web\View */
/* @var $model \common\modules\blog\forms\BlogPostForm */
$title = Yii::t('blog', 'Create Post');
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Posts'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $title;
?>
<div class="post-create">
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

76
common/modules/blog/views/manage/post/index.php

@ -0,0 +1,76 @@
<?php
use common\modules\blog\entities\BlogPost;
use common\modules\blog\helpers\BlogPostHelper;
use yii\helpers\Html;
use yii\grid\ActionColumn;
use yii\grid\GridView;
/* @var $this yii\web\View */
/* @var $searchModel \common\modules\blog\forms\search\BlogPostSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$title = Yii::t('blog', 'All Posts');
$this->title = $title;
$this->params['breadcrumbs'][] = $title;
?>
<div class="blog-post-index">
<p>
<?= Html::a(Yii::t('blog', 'Create Post'), ['create'], ['class' => 'btn btn-success']) ?>
</p>
<div class="box">
<div class="box-body">
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
[
'attribute' => 'id',
'options' => ['style' => 'width: 40px;'],
'contentOptions' => ['class' => 'text-center'],
],
[
'attribute' => 'title',
'value' => function (BlogPost $model) {
return Html::a(Html::encode($model->title), ['view', 'id' => $model->id]);
},
'format' => 'raw',
],
[
'attribute' => 'category_id',
'filter' => $searchModel->categoriesList(),
'value' => 'category.name',
],
[
'attribute' => 'published_at',
'format' => ['datetime', 'php:d.m.Y H:i'],
'options' => ['style' => 'width: 60px;'],
'contentOptions' => ['class' => 'text-center'],
],
[
'attribute' => 'status',
'filter' => $searchModel->statusList(),
'value' => function (BlogPost $model) {
return BlogPostHelper::statusLabel($model->status);
},
'format' => 'raw',
'options' => ['style' => 'width: 120px;'],
'contentOptions' => ['class' => 'text-center'],
],
[
'class' => ActionColumn::class,
/*'urlCreator' => function($action, BlogPost $model, $key, $index, $column) {
$params = is_array($key) ? $key : ['id' => (string) $key];
$params[0] = $column->controller ? $column->controller . '/' . $action : $action;
return Url::toRoute($params);
},*/
'options' => ['style' => 'width: 100px;'],
'contentOptions' => ['class' => 'text-center'],
],
],
]); ?>
</div>
</div>
</div>

21
common/modules/blog/views/manage/post/update.php

@ -0,0 +1,21 @@
<?php
/* @var $this yii\web\View */
/* @var $post \common\modules\blog\entities\BlogPost */
/* @var $model \common\modules\blog\forms\BlogPostForm */
use yii\helpers\StringHelper;
$title = Yii::t('blog', 'Update Post: {name}', ['name' => StringHelper::truncateWords($post->title, 6, '...')]);
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Posts'), 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => StringHelper::truncateWords($post->title, 4, '...'), 'url' => ['view', 'id' => $post->id]];
$this->params['breadcrumbs'][] = Yii::t('buttons', 'Editing');
?>
<div class="post-update">
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

138
common/modules/blog/views/manage/post/view.php

@ -0,0 +1,138 @@
<?php
use common\modules\blog\helpers\BlogPostHelper;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\widgets\DetailView;
/* @var $this yii\web\View */
/* @var $post \common\modules\blog\entities\BlogPost */
/* @var $modificationsProvider yii\data\ActiveDataProvider */
$title = $post->title;
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Posts'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $title;
?>
<div class="post-view">
<p>
<?= Html::a(Yii::t('blog', 'All Posts'), ['index'], ['class' => 'btn btn-default']) ?>
<?php if ($post->isActive()): ?>
<?= Html::a(Yii::t('blog', 'Draft'), ['draft', 'id' => $post->id], ['class' => 'btn btn-primary', 'data-method' => 'post']) ?>
<?php else: ?>
<?= Html::a(Yii::t('blog', 'Activate'), ['activate', 'id' => $post->id], ['class' => 'btn btn-success', 'data-method' => 'post']) ?>
<?php endif; ?>
<?= Html::a(Yii::t('buttons', 'Edit'), ['update', 'id' => $post->id], ['class' => 'btn btn-primary']) ?>
<?= Html::a(Yii::t('buttons', 'Delete'), ['delete', 'id' => $post->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => Yii::t('buttons', 'Are you sure you want to delete this item?'),
'method' => 'post',
],
]) ?>
</p>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'Common') ?></div>
<div class="box-body">
<?= DetailView::widget([
'model' => $post,
'attributes' => [
'id',
[
'attribute' => 'status',
'value' => BlogPostHelper::statusLabel($post->status),
'format' => 'raw',
],
'title',
[
'attribute' => 'category_id',
'value' => ArrayHelper::getValue($post, 'category.name'),
],
[
'label' => Yii::t('post', 'Tags'),
'value' => implode(', ', ArrayHelper::getColumn($post->tags, 'name')),
],
],
]) ?>
</div>
</div>
<?php if ($post->image): ?>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'Image') ?></div>
<div class="box-body">
<?= Html::img($post->getThumbFileUrl('image', 'thumb_gallery_view'), [
'class' => 'thumbnail',
'width' => 300,
]) ?>
</div>
</div>
<?php endif; ?>
<?php if (!$post->image && $post->video): ?>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'Image') ?></div>
<div class="box-body">
<?= Html::img('https://i.ytimg.com/vi/' . BlogPostHelper::parseYoutubeUrl($post->video) . '/maxresdefault.jpg', [
'width' => 300,
'class' => 'thumbnail',
]) ?>
</div>
</div>
<?php endif; ?>
<?php if ($post->video): ?>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'Video') ?></div>
<div class="box-body">
<iframe class="embed-responsive-item" src="https://www.youtube.com/embed/<?= BlogPostHelper::parseYoutubeUrl($post->video) ?>?rel=0" frameborder="0" allowfullscreen></iframe>
</div>
</div>
<?php endif; ?>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'Description') ?></div>
<div class="box-body">
<?= Yii::$app->formatter->asNtext($post->description) ?>
</div>
</div>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'Content') ?></div>
<div class="box-body">
<?= Yii::$app->formatter->asHtml($post->content, [
'Attr.AllowedRel' => array('nofollow'),
'HTML.SafeObject' => true,
'Output.FlashCompat' => true,
'HTML.SafeIframe' => true,
'URI.SafeIframeRegexp'=>'%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%',
]) ?>
</div>
</div>
<div class="box">
<div class="box-header with-border"><?= Yii::t('blog', 'SEO') ?></div>
<div class="box-body">
<?= DetailView::widget([
'model' => $post,
'attributes' => [
[
'attribute' => 'meta.title',
'value' => $post->meta->title,
],
[
'attribute' => 'meta.description',
'value' => $post->meta->description,
],
[
'attribute' => 'meta.keywords',
'value' => $post->meta->keywords,
],
],
]) ?>
</div>
</div>
</div>

29
common/modules/blog/views/manage/tag/_form.php

@ -0,0 +1,29 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $model \common\modules\blog\forms\BlogTagSingleForm */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="tag-form">
<?php $form = ActiveForm::begin(); ?>
<div class="box box-default">
<div class="box-body">
<?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'slug')->textInput(['maxlength' => true]) ?>
</div>
</div>
<div class="form-group">
<?= Html::submitButton(Yii::t('buttons', 'Save'), ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

17
common/modules/blog/views/manage/tag/create.php

@ -0,0 +1,17 @@
<?php
/* @var $this yii\web\View */
/* @var $model \common\modules\blog\forms\BlogTagSingleForm */
$title = Yii::t('blog', 'Create Tag');
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Tags'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="tag-create">
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

47
common/modules/blog/views/manage/tag/index.php

@ -0,0 +1,47 @@
<?php
use common\modules\blog\entities\BlogTag;
use yii\grid\ActionColumn;
use yii\helpers\Html;
use yii\grid\GridView;
/* @var $this yii\web\View */
/* @var $searchModel \common\modules\blog\forms\search\BlogTagSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$title = Yii::t('blog', 'Tags');
$this->title = $title;
$this->params['breadcrumbs'][] = $title;
?>
<div class="blog-tags-index">
<p>
<?= Html::a(Yii::t('blog', 'Create Tag'), ['create'], ['class' => 'btn btn-success']) ?>
</p>
<div class="box">
<div class="box-body">
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
[
'attribute' => 'name',
'value' => function (BlogTag $model) {
return Html::a(Html::encode($model->name), ['view', 'id' => $model->id]);
},
'format' => 'raw',
],
[
'attribute' => 'slug',
],
[
'class' => ActionColumn::class,
'options' => ['style' => 'width: 100px;'],
'contentOptions' => ['class' => 'text-center'],
],
],
]); ?>
</div>
</div>
</div>

19
common/modules/blog/views/manage/tag/update.php

@ -0,0 +1,19 @@
<?php
/* @var $this yii\web\View */
/* @var $tag \common\modules\blog\entities\BlogTag */
/* @var $model \common\modules\blog\forms\BlogTagSingleForm */
$title = Yii::t('blog', 'Update Tag: {name}', ['name' => $tag->name]);
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Tags'), 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $tag->name, 'url' => ['view', 'id' => $tag->id]];
$this->params['breadcrumbs'][] = Yii::t('buttons', 'Editing');
?>
<div class="tag-update">
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

39
common/modules/blog/views/manage/tag/view.php

@ -0,0 +1,39 @@
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
/* @var $this yii\web\View */
/* @var $tag \common\modules\blog\entities\BlogTag */
$title = $tag->name;
$this->title = $title;
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Tags'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $title;
?>
<div class="user-view">
<p>
<?= Html::a(Yii::t('blog', 'Tags'), ['index'], ['class' => 'btn btn-default']) ?>
<?= Html::a(Yii::t('buttons', 'Edit'), ['update', 'id' => $tag->id], ['class' => 'btn btn-primary']) ?>
<?= Html::a(Yii::t('buttons', 'Delete'), ['delete', 'id' => $tag->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => Yii::t('buttons', 'Are you sure you want to delete this item?'),
'method' => 'post',
],
]) ?>
</p>
<div class="box">
<div class="box-body">
<?= DetailView::widget([
'model' => $tag,
'attributes' => [
'name',
'slug',
],
]) ?>
</div>
</div>
</div>

42
common/modules/blog/views/post/_list.php

@ -0,0 +1,42 @@
<?php
/* @var $this yii\web\View */
/* @var $dataProvider yii\data\DataProviderInterface */
?>
<article class="heading-component">
<div class="heading-component-inner">
<h5 class="heading-component-title"><?= Yii::t('blog', 'All Posts') ?></h5>
</div>
</article>
<?= \yii\widgets\ListView::widget([
'dataProvider' => $dataProvider,
'layout' => "<div class='row row-30'>{items}</div>\n<nav class='pagination-wrap' aria-label='Page navigation'>{pager}</nav>",
'itemView' => '_post',
'pager' => [
//'pagination' => null,
'maxButtonCount' => 7,
'pageCssClass' => 'page-item',
'options' => [
'class' => 'pagination',
],
'disabledListItemSubTagOptions' => ['tag' => 'div', 'class' => 'disabled'],
'activePageCssClass' => 'active',
'linkOptions' => [
'class' => 'page-link',
],
'prevPageLabel' => '<span class="fa fa-angle-left"></span>',
'nextPageLabel' => '<span class="fa fa-angle-right"></span>',
],
'itemOptions' => [
'tag' => false
],
'options' => [
'tag' => false,
'class' => 'row row-30',
'id' => false,
],
]) ?>

51
common/modules/blog/views/post/_post.php

@ -0,0 +1,51 @@
<?php
/* @var $this yii\web\View */
/* @var $model \common\modules\blog\entities\BlogPost */
use yii\helpers\Html;
use yii\helpers\Url;
use yii\helpers\StringHelper;
$url = Url::to(['blog/post/post', 'id' =>$model->id]);
$url_full = Yii::$app->params['frontendHostInfo'] . Url::to(['blog/post/post', 'id' =>$model->id]);
?>
<div class="col-md-6">
<article class="post-future">
<a class="post-future-figure" href="<?= $url ?>">
<img src="<?= Html::encode($model->getThumbFileUrl('image', '368_287')) ?>" alt="<?= Html::encode($model->title) ?>" />
</a>
<div class="post-future-main">
<h4 class="post-future-title"><a href="<?= $url ?>"><?= Html::encode($model->title) ?></a></h4>
<div class="post-future-meta">
<!-- Badge-->
<div class="badge badge-secondary"><?= $model->category->name ?></div>
<div class="post-future-time"><span class="icon fa fa-calendar"></span>
<time datetime="<?= date('Y') ?>"><?= Yii::$app->formatter->asDate($model->published_at, 'php:d F, Y') ?></time>
</div>
</div>
<hr/>
<div class="post-future-text">
<p><?= StringHelper::truncateWords(Html::encode($model->description), 12, '...') ?></p>
</div>
<div class="post-future-footer group-flex group-flex-xs"><a class="button button-gray-outline" href="#"><?= Yii::t('blog', 'Read more') ?></a>
<div class="post-future-share">
<div class="inline-toggle-parent">
<div class="inline-toggle icon fa fa-share-alt" title="<?= Yii::t('blog', 'Share') ?>"></div>
<div class="inline-toggle-element">
<ul class="list-inline">
<li><span class="fa fa-share-alt"></span></li>
<li><a target="_blank" class="icon fa fa-facebook" href="https://www.facebook.com/sharer.php?src=sp&u=<?= urlencode($url_full) ?>"></a></li>
<li><a target="_blank" class="icon fa fa-odnoklassniki" href="https://connect.ok.ru/offer?url=<?= urlencode($url_full) ?>"></a></li>
<li><a target="_blank" class="icon fa fa-google-plus" href="https://plus.google.com/share?url=<?= urlencode($url_full) ?>"></a></li>
<li><a target="_blank" class="icon fa fa-vk" href="https://vk.com/share.php?url=<?= urlencode($url_full) ?>"></a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</article>
</div>

40
common/modules/blog/views/post/category.php

@ -0,0 +1,40 @@
<?php
/* @var $this yii\web\View */
/* @var $dataProvider yii\data\DataProviderInterface */
/* @var $category \common\modules\blog\entities\BlogCategory */
use yii\helpers\Html;
$this->title = $category->getSeoTitle();
$this->registerMetaTag(['name' =>'description', 'content' => $category->meta->description]);
$this->registerMetaTag(['name' =>'keywords', 'content' => $category->meta->keywords]);
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Blog'), 'url' => ['blog/post/index']];
$this->params['breadcrumbs'][] = $category->name;
$this->params['active_category'] = $category;
?>
<h1><?= Html::encode($category->getHeadingTile()) ?></h1>
<?php if (trim($category->description)): ?>
<div class="panel panel-default">
<div class="panel-body">
<?= Yii::$app->formatter->asHtml($category->description, [
'Attr.AllowedRel' => array('nofollow'),
'HTML.SafeObject' => true,
'Output.FlashCompat' => true,
'HTML.SafeIframe' => true,
'URI.SafeIframeRegexp'=>'%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%',
]) ?>
</div>
</div>
<?php endif; ?>
<?= $this->render('_list', [
'dataProvider' => $dataProvider
]) ?>

13
common/modules/blog/views/post/index.php

@ -0,0 +1,13 @@
<?php
/* @var $this yii\web\View */
/* @var $dataProvider yii\data\DataProviderInterface */
$this->title = Yii::t('blog', 'Blog');
$this->params['breadcrumbs'][] = $this->title;
?>
<?= $this->render('_list', [
'dataProvider' => $dataProvider
]) ?>

114
common/modules/blog/views/post/post.php

@ -0,0 +1,114 @@
<?php
/* @var $this yii\web\View */
/* @var $post \common\modules\blog\entities\BlogPost */
use frontend\widgets\post\CommentsWidget;
use yii\helpers\Html;
use yii\helpers\Url;
$this->title = $post->getSeoTitle();
$this->registerMetaTag(['name' =>'description', 'content' => $post->meta->description]);
$this->registerMetaTag(['name' =>'keywords', 'content' => $post->meta->keywords]);
$this->params['breadcrumbs'][] = ['label' => Yii::t('blog', 'Blog'), 'url' => ['blog/post/index']];
$this->params['breadcrumbs'][] = ['label' => $post->category->name, 'url' => ['blog/post/category', 'id' => $post->category->id]];
$this->params['breadcrumbs'][] = $post->title;
$this->params['active_category'] = $post->category;
$tagLinks = [];
foreach ($post->tags as $tag) {
$tagLinks[] = '<li>' . Html::a(Html::encode($tag->name), ['tag', 'slug' => $tag->slug]) . '</li>';
}
$url = Url::to(['blog/post/post', 'id' =>$post->id]);
$url_full = Yii::$app->params['frontendHostInfo'] . Url::to(['blog/post/post', 'id' =>$post->id]);
$js = <<<JS
jQuery(document).ready(function($) {
if (device.desktop() || device.tablet()) {
var relative_slider = $('#relative_carousel').bxSlider({
infiniteLoop: true,
hideControlOnEnd: true,
minSlides: 2,
maxSlides: 2,
moveSlides: 1,
slideMargin: 0,
pager: false,
prevText: '',
nextText: '',
controls: false
});
$('#relative-button-prev').on('click', function(){
relative_slider.goToPrevSlide();
return false;
});
$('#relative-button-next').on('click', function(){
relative_slider.goToNextSlide();
return false;
})
}
});
JS;
$this->registerJs($js, $this::POS_READY);
?>
<div class="blog-post">
<!-- Badge-->
<div class="badge badge-secondary"><?= $post->category->name ?>
</div>
<h3 class="blog-post-title"><?= Html::encode($post->title) ?></h3>
<div class="blog-post-header">
<div class="blog-post-meta">
<time class="blog-post-time" datetime="<?= date('Y') ?>"><span class="icon fa fa-calendar"></span><?= Yii::$app->formatter->asDate($post->published_at, 'php:d F, Y') ?></time>
<div class="blog-post-comment"><span class="icon fa fa-comment"></span><?= $post->comments_count ?></div>
<div class="blog-post-view"><span class="icon fa fa-eye"></span><?= $post->views ?></div>
</div>
</div>
<div class="blog-post-author-quote">
<p><?= $post->description ?></p>
</div>
<div class="blog-post-share">
<p><?= Yii::t('blog', 'Share') ?></p>
<ul class="group">
<li><a target="_blank" class="icon fa fa-facebook" href="https://www.facebook.com/sharer.php?src=sp&u=<?= urlencode($url_full) ?>"></a></li>
<li><a target="_blank" class="icon fa fa-odnoklassniki" href="https://connect.ok.ru/offer?url=<?= urlencode($url_full) ?>"></a></li>
<li><a target="_blank" class="icon fa fa-google-plus" href="https://plus.google.com/share?url=<?= urlencode($url_full) ?>"></a></li>
<li><a target="_blank" class="icon fa fa-vk" href="https://vk.com/share.php?url=<?= urlencode($url_full) ?>"></a></li>
</ul>
</div>
<div class="blog-post-content">
<img src="<?= Html::encode($post->getThumbFileUrl('image', '683_407')) ?>" alt="<?= Html::encode($post->title) ?>" width="683" height="407" />
<?= Yii::$app->formatter->asHtml($post->content, [
'Attr.AllowedRel' => array('nofollow'),
'HTML.SafeObject' => true,
'Output.FlashCompat' => true,
'HTML.SafeIframe' => true,
'URI.SafeIframeRegexp'=>'%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%',
]) ?>
</div>
</div>
<?= \frontend\widgets\post\PostByTagsWidget::widget([
'count' => 10,
'view' => 'slider-relative',
'post' => $post,
]) ?>
<ul class="list-tags" style="margin-top:20px"><?= implode(', ', $tagLinks) ?></ul>
<?= CommentsWidget::widget([
'post' => $post,
]) ?>

29
common/modules/blog/widgets/CategoriesWidget.php

@ -0,0 +1,29 @@
<?php
namespace common\modules\blog\widgets;
use common\modules\blog\entities\BlogCategory;
use common\modules\blog\repositories\read\BlogCategoryReadRepository;
use yii\base\Widget;
use yii\helpers\Html;
class CategoriesWidget extends Widget
{
/** @var BlogCategory|null */
public $active;
private $categories;
public function __construct(BlogCategoryReadRepository $categories, $config = [])
{
parent::__construct($config);
$this->categories = $categories;
}
public function run(): string
{
return $this->render('categories/categories', [
'categories' => $this->categories->getAll(),
]);
}
}

20
common/modules/blog/widgets/CommentView.php

@ -0,0 +1,20 @@
<?php
namespace common\modules\blog\widgets;
use common\modules\blog\entities\BlogComment;
class CommentView
{
public $comment;
/**
* @var self[]
*/
public $children;
public function __construct(BlogComment $comment, array $children)
{
$this->comment = $comment;
$this->children = $children;
}
}

57
common/modules/blog/widgets/CommentsWidget.php

@ -0,0 +1,57 @@
<?php
namespace common\modules\blog\widgets;
use common\modules\blog\entities\BlogComment;
use common\modules\blog\entities\BlogPost;
use common\modules\blog\forms\BlogCommentForm;
use yii\base\InvalidConfigException;
use yii\base\Widget;
class CommentsWidget extends Widget
{
/**
* @var BlogPost
*/
public $post;
public function init(): void
{
if (!$this->post) {
throw new InvalidConfigException('Specify the post.');
}
}
public function run(): string
{
$form = new BlogCommentForm();
$comments = $this->post->getBlogComments()
->orderBy(['parent_id' => SORT_ASC, 'id' => SORT_ASC])
->all();
$items = $this->treeRecursive($comments, null);
return $this->render('comments/comments', [
'post' => $this->post,
'items' => $items,
'commentForm' => $form,
]);
}
/**
* @param BlogComment[] $comments
* @param integer $parentId
* @return CommentView[]
*/
public function treeRecursive(&$comments, $parentId): array
{
$items = [];
foreach ($comments as $comment) {
if ($comment->parent_id == $parentId) {
$items[] = new CommentView($comment, $this->treeRecursive($comments, $comment->id));
}
}
return $items;
}
}

39
common/modules/blog/widgets/TagWidget.php

@ -0,0 +1,39 @@
<?php
/**
* Created by Error202
* Date: 30.01.2018
*/
namespace common\modules\blog\widgets;
use common\modules\blog\entities\BlogPost;
use yii\base\Widget;
class TagWidget extends Widget
{
public $post_id;
public function run() {
// tags from categories with assignments, ordered by count
$tags = BlogPost::find()->select('t.name as name, count(t.id) as cnt, t.id as id')
->from('blog_posts as p, blog_tag_assignments as a, blog_tags as t')
->andWhere('p.id = a.post_id')
->andWhere('t.id = a.tag_id')
->andWhere(isset($this->post_id) ? 'p.id = ' . $this->post_id : '')
->groupBy('t.id')
->orderBy('cnt DESC')
->limit(20)
->asArray()
->all();
if ($tags) {
return $this->render( 'tags/tags', [
'tags' => $tags,
] );
}
else {
return '';
}
}
}

32
common/modules/blog/widgets/views/categories/categories.php

@ -0,0 +1,32 @@
<?php
/**
* Created by Error202
* Date: 21.06.2018
*/
use yii\helpers\Html;
/**
* @var $this \yii\web\View
* @var $categories \common\modules\blog\entities\BlogCategory[]
*/
?>
<!-- Categories Widget -->
<div class="card my-4">
<h5 class="card-header"><?= Yii::t('blog_public', 'Categories') ?></h5>
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<ul class="list-unstyled mb-0 blog-categories-widget-list">
<?php foreach ($categories as $category): ?>
<li>
<?= Html::a($category->name, ['/blog/post/category', 'id' => $category->id]) ?>
</li>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>
</div>

82
common/modules/blog/widgets/views/comments/_comment.php

@ -0,0 +1,82 @@
<?php
use core\components\avatar_generator\AvatarGenerator;
/* @var $item \common\modules\blog\widgets\CommentView */
/* @var $child integer */
$avatar = new AvatarGenerator();
?>
<div data-id="<?= $item->comment->id ?>" class="blog-comment <?= $child ? 'blog-comment-child' : 'blog-comment-parent' ?>" style="margin-left: <?= $child * 30 ?>px">
<div class="media mb-4">
<img class="d-flex mr-3 rounded-circle blog-comment-avatar" src="<?= $avatar->avatar($item->comment->user->id) ?>" alt="">
<div class="media-body">
<a name="comment_<?= $item->comment->id ?>"></a>
<h5 class="mt-0"><?= $item->comment->user->username ?>
<time class="blog-comment-time" datetime="<?= date('Y', $item->comment->created_at) ?>">
<?php
$date = ($item->comment->created_at > time() - 60*60*24*365*50
? Yii::$app->formatter->asRelativeTime($item->comment->created_at)
: Yii::$app->formatter->asDatetime($item->comment->created_at));
?>
<?= $date ?>
</time>
</h5>
<p>
<?php if ($item->comment->isActive()): ?>
<?= Yii::$app->formatter->asNtext($item->comment->text) ?>
<?php else: ?>
<i>Comment is deleted.</i>
<?php endif; ?>
</p>
<div class="blog-comment-footer">
<?php if (!Yii::$app->user->isGuest): ?>
<div class="blog-comment-reply"><a href="#"><?= Yii::t('post', 'Reply') ?></a></div>
<?php endif; ?>
</div>
<div class="reply-block"></div>
</div>
</div>
<!--
<div class="post-comment-main">
<div class="post-comment-header">
<a name="comment_<?= $item->comment->id ?>"></a>
<h5 class="author-name"><?= $item->comment->user->username ?></h5>
<time class="post-comment-time" datetime="<?= date('Y', $item->comment->created_at) ?>">
<?php
$date = ($item->comment->created_at > time() - 60*60*24*365*50
? Yii::$app->formatter->asRelativeTime($item->comment->created_at)
: Yii::$app->formatter->asDatetime($item->comment->created_at));
?>
<?= $date ?>
</time>
</div>
<div class="post-comment-text">
<p>
<?php if ($item->comment->isActive()): ?>
<?= Yii::$app->formatter->asNtext($item->comment->text) ?>
<?php else: ?>
<i>Comment is deleted.</i>
<?php endif; ?>
</p>
</div>
<div class="post-comment-footer">
<?php if (!Yii::$app->user->isGuest): ?>
<div class="comment-reply"><span class="icon fa fa-comment"></span><a href="#"><?= Yii::t('post', 'Reply') ?></a></div>
<?php endif; ?>
</div>
<div class="reply-block"></div>
</div>-->
</div>
<?php foreach ($item->children as $children): ?>
<?= $this->render('_comment', ['item' => $children, 'child' => $child + 1]) ?>
<?php endforeach; ?>

103
common/modules/blog/widgets/views/comments/comments.php

@ -0,0 +1,103 @@
<?php
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $post GuzzleHttp\Post\ */
/* @var $items \common\modules\blog\widgets\CommentView[] */
/* @var $count integer */
/* @var $commentForm \common\modules\blog\forms\BlogCommentForm */
$css = <<<CSS
a.reply-block-start {
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
a#send-comment {
font-size: 14px;
font-weight: normal;
}
.heading-component {
margin-bottom: 20px;
}
CSS;
$this->registerCss( $css );
?>
<div class="row">
<div class="col-sm-12">
<!-- Heading Component-->
<article class="heading-component">
<div class="heading-component-inner">
<h5 class="heading-component-title">
<?= Yii::t( 'post', 'Comments: {count}', [ 'count' => $post->comments_count ] ) ?>
<?php if ( ! Yii::$app->user->isGuest ): ?>
<a id="send-comment" href="#reply-block-start"
class="pull-right"><?= Yii::t( 'post', 'Send comment' ) ?></a>
<?php endif; ?>
</h5>
</div>
</article>
<?php if ( count( $items ) > 0 ): ?>
<div class="blog-post-comments">
<?php foreach ( $items as $item ): ?>
<?= $this->render( '_comment', [ 'item' => $item, 'child' => 0 ] ) ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ( ! Yii::$app->user->isGuest ): ?>
<div id="reply-container">
<a name="reply-block-start" class="reply-block-start"></a>
<div id="reply-block" class="leave-reply">
<?php $form = ActiveForm::begin( [
'action' => [ 'comment', 'id' => $post->id ],
] ); ?>
<?= Html::activeHiddenInput( $commentForm, 'parentId' ) ?>
<?= $form->field( $commentForm, 'text' )->textarea( [ 'rows' => 5 ] )->label( Yii::t( 'post', 'Comment' ) ) ?>
<div class="form-group">
<?= Html::submitButton( Yii::t( 'post', 'Submit' ), [ 'class' => 'btn btn-primary' ] ) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
<?php else: ?>
<div style="text-align: center; padding: 20px;">
<?= Yii::t('post', 'Please {login} for writing a comment.', ['login' => Html::a(Yii::t('auth', 'Log in'), ['/auth/auth/login'])]) ?>
</div>
<?php endif; ?>
</div>
</div>
<?php $this->registerJs( "
jQuery(document).on('click', '.blog-post-comments .blog-comment-reply', function () {
var link = jQuery(this);
var form = jQuery('#reply-block');
var comment = link.closest('.blog-comment');
jQuery('#blogcommentform-parentid').val(comment.data('id'));
form.detach().appendTo(comment.find('.reply-block:first'));
return false;
});
jQuery(document).on('click', '#send-comment', function () {
var form = jQuery('#reply-block');
jQuery('#blogcommentform-parentid').val('');
form.detach().appendTo('#reply-container');
//return false;
});
" ); ?>

21
common/modules/blog/widgets/views/tags/tags.php

@ -0,0 +1,21 @@
<?php
/**
* Created by Error202
* Date: 21.06.2018
*/
use yii\helpers\Html;
/**
* @var $this \yii\web\View
* @var $tags array
*/
?>
<hr>
<ul class="list-unstyled">
<?php foreach ($tags as $tag): ?>
<li class="list-inline-item"><i class="fa fa-tag" aria-hidden="true"></i> <?= Html::a($tag['name'], ['/blog/post/tag', 'id' => $tag['id']]) ?></li>
<?php endforeach; ?>
</ul>

2
common/runtime/.gitignore vendored

@ -0,0 +1,2 @@
*
!.gitignore

1
common/runtime/cache/00/003d3baedc022e4d084a532960a55cc3.bin vendored

@ -1 +0,0 @@
a:2:{i:0;a:2:{s:2:"id";N;s:4:"path";N;}i:1;N;}

1
common/runtime/cache/00/00431a2d632e2abcfe7139037442a6ef.bin vendored

@ -1 +0,0 @@
a:2:{i:0;a:2:{s:2:"id";N;s:4:"path";N;}i:1;N;}

1
common/runtime/cache/00/0045808e17fdda07b6043f2858e01289.bin vendored

@ -1 +0,0 @@
a:2:{i:0;a:2:{s:3:"tid";N;s:4:"path";N;}i:1;N;}

1
common/runtime/cache/00/005ab8900f97feeb9ab462ab8b45a4b2.bin vendored

@ -1 +0,0 @@
a:2:{i:0;a:2:{s:3:"tid";N;s:4:"path";N;}i:1;N;}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save