.
diff --git a/application/plugins/topicsimilar/PluginTopicsimilar.class.php b/application/plugins/topicsimilar/PluginTopicsimilar.class.php
new file mode 100644
index 00000000..f14b75cb
--- /dev/null
+++ b/application/plugins/topicsimilar/PluginTopicsimilar.class.php
@@ -0,0 +1,37 @@
+ [
+ 'ModuleTopic'
+ ],
+ 'mapper' => [
+ 'ModuleTopic_MapperTopic'
+ ],
+ ];
+
+ /**
+ * Инициализация плагина
+ */
+ public function Init()
+ {
+ /**
+ * Подключаем CSS
+ */
+ $this->Viewer_AppendStyle(Plugin::GetTemplatePath(__CLASS__) . 'assets/css/style.css');
+ return true;
+ }
+}
diff --git a/application/plugins/topicsimilar/README.md b/application/plugins/topicsimilar/README.md
new file mode 100644
index 00000000..1c94430c
--- /dev/null
+++ b/application/plugins/topicsimilar/README.md
@@ -0,0 +1,16 @@
+Topic Similar plugin for [LiveStreet CMS](http://livestreetcms.com/ "LiveStreet CMS")
+=====================================================================================
+
+Требования
+----------
+LiveStreet CMS версии **2.0.0** или более поздней.
+
+
+Описание
+--------
+Добавляет на страницу топика блок с похожими топиками по тегам.
+
+
+Лицензия
+--------
+[GPL-3.0](https://opensource.org/licenses/GPL-3.0)
diff --git a/application/plugins/topicsimilar/classes/.htaccess b/application/plugins/topicsimilar/classes/.htaccess
new file mode 100644
index 00000000..2859d7f4
--- /dev/null
+++ b/application/plugins/topicsimilar/classes/.htaccess
@@ -0,0 +1,2 @@
+Order Deny,Allow
+Deny from all
\ No newline at end of file
diff --git a/application/plugins/topicsimilar/classes/blocks/BlockSimilar.class.php b/application/plugins/topicsimilar/classes/blocks/BlockSimilar.class.php
new file mode 100644
index 00000000..879ceef8
--- /dev/null
+++ b/application/plugins/topicsimilar/classes/blocks/BlockSimilar.class.php
@@ -0,0 +1,39 @@
+GetParam('topic')) {
+ $aTags = $oTopic->getTagsArray();
+ $aExcludeTopic[] = $oTopic->getId();
+ } else {
+ $aTags = $this->GetParam('tags');
+ }
+ /**
+ * Топики по тегам
+ */
+ $aSimilarTopics = $this->Topic_GetTopicsSimilarByTags((array)$aTags, $aExcludeTopic, Config::Get('plugin.topicsimilar.block.row'));
+ $this->Viewer_Assign('aSimilarTopics', $aSimilarTopics);
+ }
+
+}
diff --git a/application/plugins/topicsimilar/classes/hooks/HookMain.class.php b/application/plugins/topicsimilar/classes/hooks/HookMain.class.php
new file mode 100644
index 00000000..d3e129bc
--- /dev/null
+++ b/application/plugins/topicsimilar/classes/hooks/HookMain.class.php
@@ -0,0 +1,54 @@
+AddHook('topic_show', 'TopicShow');
+ $this->AddHook('template_comments_begin', 'TplCommentsBefore');
+ $this->AddHook('template_comments_end', 'TplCommentsAfter');
+ }
+
+ public function TopicShow($aVars = [])
+ {
+ if (isset($aVars['oTopic'])) {
+ $aGroupsExists = ['right', 'comments_before', 'comments_after'];
+ if (!in_array(Config::Get('plugin.topicsimilar.position'), $aGroupsExists)) {
+ return false;
+ }
+ $sBlockGroup = Config::Get('plugin.topicsimilar.position');
+
+ /**
+ * Добавляем блок
+ */
+ $this->Viewer_AddBlock($sBlockGroup, 'similar',
+ array('plugin' => Plugin::GetPluginCode($this), 'topic' => $aVars['oTopic']),
+ (int)Config::Get('plugin.topicsimilar.block.priority'));
+ }
+ return true;
+ }
+
+ public function TplCommentsBefore()
+ {
+ return $this->Viewer_Fetch(Plugin::GetTemplatePath(__CLASS__) . 'inject.comments.before.tpl');
+ }
+
+ public function TplCommentsAfter()
+ {
+ return $this->Viewer_Fetch(Plugin::GetTemplatePath(__CLASS__) . 'inject.comments.after.tpl');
+ }
+}
diff --git a/application/plugins/topicsimilar/classes/modules/topic/Topic.class.php b/application/plugins/topicsimilar/classes/modules/topic/Topic.class.php
new file mode 100644
index 00000000..eeea992e
--- /dev/null
+++ b/application/plugins/topicsimilar/classes/modules/topic/Topic.class.php
@@ -0,0 +1,43 @@
+Cache_Get($sCacheKey))) {
+ $data = $this->oMapperTopic->GetTopicsIdByTagsArray($aTags, $aExcludeTopic, $iLimit, $aIncludeType);
+ $data = $this->GetTopicsAdditionalData($data, [ 'user' => [] ]);
+ // Убираем будущие публикации из предложений
+ foreach ($data as $index => $oTopic) {
+ $date = (DateTime::createFromFormat('Y-m-d H:i:s', $oTopic->getTopicDatePublish()))->format('U');
+ if ($date > time()) {
+ unset($data[$index]);
+ continue;
+ }
+ }
+ $this->Cache_Set($data, $sCacheKey, array('topic_update', 'topic_new'), 60 * 60 * 24 * 3);
+ }
+ return $data;
+ }
+
+}
diff --git a/application/plugins/topicsimilar/classes/modules/topic/mapper/Topic.mapper.class.php b/application/plugins/topicsimilar/classes/modules/topic/mapper/Topic.mapper.class.php
new file mode 100644
index 00000000..c14b6e47
--- /dev/null
+++ b/application/plugins/topicsimilar/classes/modules/topic/mapper/Topic.mapper.class.php
@@ -0,0 +1,60 @@
+oDb->select(
+ $sql, $aTags,
+ (is_array($aExcludeTopic) && count($aExcludeTopic)) ? $aExcludeTopic : DBSIMPLE_SKIP,
+ (is_array($aIncludeType) && count($aIncludeType)) ? $aIncludeType : DBSIMPLE_SKIP,
+ $iLimit
+ )
+ ) {
+ foreach ($aRows as $aRow) {
+ $aResult[] = $aRow['topic_id'];
+ }
+ }
+ }
+ return $aResult;
+ }
+
+}
diff --git a/application/plugins/topicsimilar/config/.htaccess b/application/plugins/topicsimilar/config/.htaccess
new file mode 100644
index 00000000..c3c2d19d
--- /dev/null
+++ b/application/plugins/topicsimilar/config/.htaccess
@@ -0,0 +1,2 @@
+Order Deny,Allow
+Deny from all
diff --git a/application/plugins/topicsimilar/config/config.php b/application/plugins/topicsimilar/config/config.php
new file mode 100644
index 00000000..34f8f6a1
--- /dev/null
+++ b/application/plugins/topicsimilar/config/config.php
@@ -0,0 +1,29 @@
+ 'comments_before',
+ /**
+ * Настройка блока
+ */
+ 'block' => [
+ 'row' => 5, // кол-во выводимыз записей
+ 'priority' => 300 // приоритет блока
+ ],
+
+];
diff --git a/application/plugins/topicsimilar/config/scheme.php b/application/plugins/topicsimilar/config/scheme.php
new file mode 100644
index 00000000..03366a23
--- /dev/null
+++ b/application/plugins/topicsimilar/config/scheme.php
@@ -0,0 +1,111 @@
+ [
+ 'position' => [
+ /*
+ * тип: integer, string, array, boolean, float
+ */
+ 'type' => 'string',
+ /*
+ * отображаемое имя параметра, ключ языкового файла
+ */
+ 'name' => 'config.position.name',
+ /*
+ * отображаемое описание параметра, ключ языкового файла
+ */
+ 'description' => 'config.position.description',
+ /*
+ * валидатор (не обязательно)
+ */
+ 'validator' => [
+ /*
+ * тип валидатора, существующие типы валидаторов движка:
+ * Boolean, Compare, Date, Email, Number, Regexp, Required, String, Tags, Type, Url, дополнительные: Array и Enum (специальные валидаторы, см. документацию)
+ */
+ 'type' => 'Enum',
+ /*
+ * параметры, которые будут переданы в валидатор
+ */
+ 'params' => [
+ 'enum' => [
+ 'comments_before',
+ 'comments_after',
+ 'right',
+ ],
+ 'allowEmpty' => false,
+ ],
+ ]
+ ],
+ 'block.row' => [
+ 'type' => 'integer',
+ 'name' => 'config.block.row.name',
+ 'description' => 'config.block.row.description',
+ 'validator' => array(
+ 'type' => 'Number',
+ 'params' => array(
+ 'min' => 1,
+ 'max' => 100,
+ 'integerOnly' => true,
+ 'allowEmpty' => false,
+ ),
+ ),
+ ],
+ 'block.priority' => [
+ 'type' => 'integer',
+ 'name' => 'config.block.priority.name',
+ 'description' => 'config.block.priority.description',
+ 'validator' => array(
+ 'type' => 'Number',
+ 'params' => array(
+ 'integerOnly' => true,
+ 'allowEmpty' => false,
+ ),
+ ),
+ ],
+ ],
+ /**
+ * Описание разделов для настроек
+ * Каждый раздел группирует определенные параметры конфига
+ */
+ '$config_sections$' => [
+ /**
+ * Основные настройки
+ */
+ [
+ /**
+ * Название раздела
+ */
+ 'name' => 'config_sections.main.name',
+ 'description' => 'config_sections.main.description',
+ /**
+ * Список параметров для отображения в разделе
+ */
+ 'allowed_keys' => [
+ 'position'
+ ],
+ ],
+ /**
+ * Настройки Блока
+ */
+ [
+ 'name' => 'config_sections.block.name',
+ 'description' => 'config_sections.block.description',
+ 'allowed_keys' => [
+ 'block.row', 'block.priority',
+ ],
+ ],
+ ]
+];
diff --git a/application/plugins/topicsimilar/frontend/.htaccess b/application/plugins/topicsimilar/frontend/.htaccess
new file mode 100644
index 00000000..a5b8195f
--- /dev/null
+++ b/application/plugins/topicsimilar/frontend/.htaccess
@@ -0,0 +1,5 @@
+Options -Indexes
+
+ Order allow,deny
+ Deny from all
+
\ No newline at end of file
diff --git a/application/plugins/topicsimilar/frontend/i18n/en.php b/application/plugins/topicsimilar/frontend/i18n/en.php
new file mode 100644
index 00000000..c6480baf
--- /dev/null
+++ b/application/plugins/topicsimilar/frontend/i18n/en.php
@@ -0,0 +1,45 @@
+ [
+ 'position' => [
+ 'name' => 'Position',
+ 'description' => 'Before comments, After comments, Sidebar',
+ ],
+ 'block' => [
+ 'row' => [
+ 'name' => 'Count topics per block',
+ 'description' => 'from 1 to 100',
+ ],
+ 'priority' => [
+ 'name' => 'Block priority',
+ 'description' => '',
+ ],
+ ],
+ ],
+ 'config_sections' => [
+ 'main' => [
+ 'name' => 'Main settings',
+ 'description' => '',
+ ],
+ 'block' => [
+ 'name' => 'Block settings',
+ 'description' => '',
+ ],
+ ],
+ 'block' => [
+ 'title' => 'Similar topics',
+ ]
+];
\ No newline at end of file
diff --git a/application/plugins/topicsimilar/frontend/i18n/ru.php b/application/plugins/topicsimilar/frontend/i18n/ru.php
new file mode 100644
index 00000000..ef9c2de4
--- /dev/null
+++ b/application/plugins/topicsimilar/frontend/i18n/ru.php
@@ -0,0 +1,45 @@
+ [
+ 'position' => [
+ 'name' => 'Позиция блока',
+ 'description' => 'Перед комментариями, После комментариев, Сайдбар сайта',
+ ],
+ 'block' => [
+ 'row' => [
+ 'name' => 'Количество выводимых записей',
+ 'description' => 'От 1 до 100',
+ ],
+ 'priority' => [
+ 'name' => 'Приоритет блока',
+ 'description' => 'Чем больше приоритет, тем выше блок по сравнению с другими блоками (актуально для позиции Сайдбар)',
+ ],
+ ],
+ ],
+ 'config_sections' => [
+ 'main' => [
+ 'name' => 'Основные настройки',
+ 'description' => '',
+ ],
+ 'block' => [
+ 'name' => 'Настройки блока',
+ 'description' => '',
+ ],
+ ],
+ 'block' => [
+ 'title' => 'Похожие публикации',
+ ]
+];
\ No newline at end of file
diff --git a/application/plugins/topicsimilar/frontend/skin/default/assets/css/style.css b/application/plugins/topicsimilar/frontend/skin/default/assets/css/style.css
new file mode 100644
index 00000000..c1e8d416
--- /dev/null
+++ b/application/plugins/topicsimilar/frontend/skin/default/assets/css/style.css
@@ -0,0 +1,52 @@
+.ls-topic-similar {
+ margin-bottom: 30px;
+ margin-top: 50px;
+}
+
+.ls-topic-similar-info {
+ list-style: none;
+ font-size: 11px;
+}
+.ls-topic-similar-info li {
+ display: inline-block;
+ margin-right: 5px;
+}
+.ls-topic-similar-user {
+ color: #4c4c4c;
+}
+.ls-topic-similar-date {
+ color: #999;
+}
+.ls-topic-similar-comments {
+ color: #666;
+}
+
+
+.ls-topic-similar .ls-block--similar-topics .ls-block-header {
+ padding-bottom: 10px;
+}
+.ls-topic-similar .ls-block--similar-topics .ls-block-header::before {
+ display: none;
+}
+.ls-topic-similar .ls-block--similar-topics .ls-item-title {
+ margin-bottom: 10px;
+ font-size: 20px;
+}
+.ls-topic-similar .ls-topic-similar-info {
+ font-size: 12px;
+}
+.ls-topic-similar .ls-topic-similar-info li {
+ margin-right: 15px;
+}
+.ls-topic-similar .ls-topic-similar-user {
+ font-weight: bold;
+}
+
+
+.ls-block--similar-topics .ls-item-title {
+ margin-bottom: 5px;
+ font-size: 16px;
+}
+.ls-block--similar-topics .ls-block-content {
+ padding: 0;
+}
diff --git a/application/plugins/topicsimilar/frontend/skin/default/blocks/block.similar.tpl b/application/plugins/topicsimilar/frontend/skin/default/blocks/block.similar.tpl
new file mode 100644
index 00000000..8769eb4b
--- /dev/null
+++ b/application/plugins/topicsimilar/frontend/skin/default/blocks/block.similar.tpl
@@ -0,0 +1,59 @@
+{**
+ * Блок "похожие топики по тегам"
+ *
+ * @param array $items
+ *}
+
+
+{capture 'similar_content'}
+ {if $aSimilarTopics}
+ {$similarItems = []}
+
+ {foreach $aSimilarTopics as $oSimilarTopic}
+ {$oSimilarTopicUser = $oSimilarTopic->getUser()}
+
+ {capture 'item_content'}
+
+ {/capture}
+
+ {$similarItems[] = [
+ 'element' => 'li',
+ 'mods' => 'image-rounded',
+ 'title' => $oSimilarTopic->getTitle(),
+ 'titleUrl'=> $oSimilarTopic->getUrl(),
+ 'content' => $smarty.capture.item_content,
+ 'image' => [
+ 'path' => $oSimilarTopicUser->getProfileAvatarPath(48),
+ 'url' => $oSimilarTopicUser->getUserWebPath()
+ ]
+ ]}
+ {/foreach}
+
+ {component 'item' template='group' items=$similarItems}
+ {else}
+ {component 'blankslate' text={lang 'common.empty'} mods='no-background'}
+ {/if}
+{/capture}
+
+
+{component 'block'
+ mods = 'similar-topics'
+ classes = 'js-block-default'
+ title = {lang 'plugin.topicsimilar.block.title'}
+ content = $smarty.capture.similar_content}
diff --git a/application/plugins/topicsimilar/frontend/skin/default/inject.comments.after.tpl b/application/plugins/topicsimilar/frontend/skin/default/inject.comments.after.tpl
new file mode 100644
index 00000000..861352a8
--- /dev/null
+++ b/application/plugins/topicsimilar/frontend/skin/default/inject.comments.after.tpl
@@ -0,0 +1,3 @@
+
+ {show_blocks group='comments_after'}
+
diff --git a/application/plugins/topicsimilar/frontend/skin/default/inject.comments.before.tpl b/application/plugins/topicsimilar/frontend/skin/default/inject.comments.before.tpl
new file mode 100644
index 00000000..3dd93f40
--- /dev/null
+++ b/application/plugins/topicsimilar/frontend/skin/default/inject.comments.before.tpl
@@ -0,0 +1,3 @@
+
+ {show_blocks group='comments_before'}
+
diff --git a/application/plugins/topicsimilar/plugin.xml b/application/plugins/topicsimilar/plugin.xml
new file mode 100644
index 00000000..2a393505
--- /dev/null
+++ b/application/plugins/topicsimilar/plugin.xml
@@ -0,0 +1,22 @@
+
+
+
+ Similar topics
+ Похожие топики
+
+
+ Chiffa
+
+ http://goweb.pro
+ {admin/plugin/topicsimilar}
+ 1.0.0
+
+ 2.0.*
+
+
+
+
+ Display a block similar topics by tags in topic page
+ Выводит блок похожих топиков по тегам на странице топика
+
+
\ No newline at end of file