update ver 3.31

This commit is contained in:
2026-03-30 22:38:23 +05:00
parent 1c9cc12544
commit a330d5dc83
5 changed files with 584 additions and 91 deletions

View File

@@ -1,14 +1,34 @@
### moredoc
## Модуль Похожие документы v1.26.0
## Модуль Похожие документы v3.31
### для AVE.CMS ALT ≥ v3.31
### Данный модуль предназначен для вывода списка похожих документов относительно текущего.
* Связующим элементом документов является первое слово из поля Ключевые слова.
* Пример работы модуля. Создайте в трех документах одинаковое первое ключевое слово ```тест```, разместите системный тег ```[mod_moredoc]``` или в каждом документе или в шаблоне рубрики которой эти документы принадлежат, посмотрите результат.
* Настройки модуля расположены в файле modules/moredoc/module.php :
* В самом начале файла есть переменная $limit = 5; // Количество связанных документов. Вы можете менять это число в любую сторону.
* Следом идет переменная $flagrubric = 1; // Учитывать или нет рубрику документа (0 - нет, 1 - да). Если 0 - будут связаны между домой все документы без учета их принадлежности к рубрикам, если 1 - связываются между собой и выводятся документы из рубрики которой они принадлежат.
* Результат вывода кешируется средствами Smarty.<BR /><BR />Для вывода списка похожих документов используйте системный тег <strong>[mod_moredoc]</strong> (можно использовать как в документах так и шаблоне рубрики).
* Важно! После каждого действия имеющего отношение к модулю <strong>обязательно чистите кеш системы</strong> иначе вы не увидите результатов - модуль жестко берет данные из кеша если кеш модуля существует.
### Список похожих документов начиная с версии модуля 3.31 выводится карточкой, верстка Bootstrap 5
* В карточке отображаются следующие параметры документа:
* Название документа берется из ```HTML <title>```
* Описание документа берется из ```(meta description)```
* Дата публикации документа
* Количество просмотров
* Изображение из ```первого найденного поля Изображение (Одиночное)```, если поле отсутствует выводится упрощенный вариант карточки без изображения.
* Верстка карточки находится в файле ```modules/moredoc/templates/moredoc.tpl``` в котором находится четыре варианта карточек, три из которых закомментированы. Для их просмотра раскомментируйте код путем удаления строк ```{* ---- начало комментария``` и ```------ конец комментария*}```
### Changelog:
30.03.2026 - обновление модуля - версия 3.31 - рефакторинг кода для работы в ave.cms ALT v3.31. Добавлены в шаблон вывода в публичной части сайта 4 карточки , верстка Bootstrap 5, так же добавлен вывод изображения, даты публикации, количество просмотров.
03.09.2019 - версия 1.26.0 - адаптация для версии ave.cms v3.26
01.03.2013 - версия 1.0

View File

@@ -4,7 +4,7 @@
$module = array(
'ModuleSysName' => 'moredoc',
'ModuleVersion' => '1.26.0',
'ModuleVersion' => '3.31',
'ModuleAutor' => 'AVE.cms Team',
'ModuleCopyright' => '&copy; 2007-' . date('Y') . ' AVE.cms',
'ModuleStatus' => 1,

View File

@@ -3,4 +3,6 @@ MODULE_NAME = "Ссылки по теме"
MODULE_DESCRIPTION = "Данный модуль предназначен для вывода списка похожих документов относительно текущего. Связующим элементом документов является первое слово из поля Ключевые слова. Результат вывода кешируется средствами Smarty.<BR /><BR />Для вывода списка похожих документов используйте системный тег <strong>[mod_moredoc]</strong> (можно использовать как в документах так и шаблоне рубрики)."
[module]
MOREDOC_NAME = "Возможно Вас заинтересует:"
MOREDOC_NAME = "Возможно Вас заинтересует:"
MOREDOC_NOIMAGE = "Нет изображения"
MOREDOC_READMORE = "Подробнее"

View File

@@ -16,96 +16,124 @@ if(!defined('BASE_DIR')) exit;
*/
function mod_moredoc()
{
global $AVE_Core, $AVE_DB, $AVE_Template;
global $AVE_Core, $AVE_DB, $AVE_Template;
require_once(BASE_DIR . '/functions/func.modulglobals.php');
set_module_globals('moredoc');
require_once(BASE_DIR . '/functions/func.modulglobals.php');
set_module_globals('moredoc');
$AVE_Template->caching = true; // Включаем кеширование
$AVE_Template->cache_lifetime = 60*60*24; // Время жизни кеша 1 день в секундах
// $AVE_Template->cache_dir .= '/moredoc'; // Папка для кеша модуля
$AVE_Template->caching = true; // Включаем кеширование
$AVE_Template->cache_lifetime = 60*60*24; // Время жизни кеша 1 день в секундах
$tpl_dir = BASE_DIR . '/modules/moredoc/templates/'; // Указываем путь к шаблону модуля
$tpl_dir = BASE_DIR . '/modules/moredoc/templates/'; // Указываем путь к шаблону модуля
// Если нету в кеше, то начинаем обрабатывать
if (!$AVE_Template->is_cached($tpl_dir . 'moredoc.tpl', $AVE_Core->curentdoc->Id))
{
$limit = 5; // Количество связных документов
$flagrubric = 1; // Учитывать или нет рубрику документа (0 - нет, 1 - да)
// Если нету в кеше, то начинаем обрабатывать
if (!$AVE_Template->is_cached($tpl_dir . 'moredoc.tpl', $AVE_Core->curentdoc->Id))
{
$limit = 5; // Количество связных документов
$flagrubric = 1; // Учитывать или нет рубрику документа (0 - нет, 1 - да)
$moredoc = array();
$moredoc = array();
// Проверяем, есть ли папка для кеша, если нет (первый раз) — создаем
if (!is_dir($AVE_Template->cache_dir))
{
$oldumask = umask(0);
@mkdir($AVE_Template->cache_dir, 0777);
umask($oldumask);
}
// Проверяем, есть ли папка для кеша, если нет (первый раз) — создаем
if (!is_dir($AVE_Template->getCacheDir()))
{
$oldumask = umask(0);
@mkdir($AVE_Template->getCacheDir(), 0777);
umask($oldumask);
}
// Получаем ключевые слова, рубрику, извлекаем первое ключевое слово
$row = $AVE_DB->Query("
SELECT
rubric_id,
document_meta_keywords
FROM " . PREFIX . "_documents
WHERE Id = '" . $AVE_Core->curentdoc->Id . "'
LIMIT 1
")->FetchRow();
// Получаем ключевые слова и ID рубрики текущего документа
$row = $AVE_DB->Query("
SELECT
rubric_id,
document_meta_keywords
FROM " . PREFIX . "_documents
WHERE Id = '" . (int)$AVE_Core->curentdoc->Id . "'
LIMIT 1
")->FetchRow();
$keywords = explode(',',$row->document_meta_keywords);
$keywords = trim($keywords[0]);
$keywords = explode(',', $row->document_meta_keywords);
$keywords = trim($keywords[0]);
if ($keywords != '')
{
$inrubric = $flagrubric ? ("AND rubric_id = '" . $row->rubric_id . "'") : '';
$doctime = get_settings('use_doctime')
? ("AND document_published <= " . time() . " AND (document_expire = 0 OR document_expire >= " . time() . ")") : '';
if ($keywords != '')
{
$inrubric = $flagrubric ? ("AND d.rubric_id = '" . (int)$row->rubric_id . "'") : '';
$doctime = get_settings('use_doctime')
? ("AND d.document_published <= " . time() . " AND (d.document_expire = 0 OR d.document_expire >= " . time() . ")") : '';
// Ищем документы где встречается такое-же слово
$sql = $AVE_DB->Query("
SELECT
Id,
document_expire,
document_title,
document_alias,
document_meta_description
FROM " . PREFIX . "_documents
WHERE document_meta_keywords LIKE '" . $keywords . "%'
AND Id != 1
AND Id != '" . PAGE_NOT_FOUND_ID . "'
AND Id != '" . $AVE_Core->curentdoc->Id . "'
AND document_status != '0'
AND document_deleted != '1'
" . $inrubric . "
" . $doctime . "
ORDER BY document_count_view DESC
LIMIT " . $limit
);
/**
* Используем подзапрос rf_first, чтобы найти минимальный ID поля image_single для каждой рубрики.
*/
$sql = $AVE_DB->Query("
SELECT
d.Id,
d.document_published,
d.document_count_view,
d.document_expire,
d.document_title,
d.document_alias,
d.document_meta_description,
df.field_value AS document_image
FROM " . PREFIX . "_documents AS d
/* Находим ID самого первого поля типа image_single для каждой рубрики */
LEFT JOIN (
SELECT rubric_id, MIN(Id) as first_img_id
FROM " . PREFIX . "_rubric_fields
WHERE rubric_field_type = 'image_single'
GROUP BY rubric_id
) AS rf_first ON (rf_first.rubric_id = d.rubric_id)
/* Подтягиваем значение именно этого конкретного поля */
LEFT JOIN " . PREFIX . "_document_fields AS df ON (
df.document_id = d.Id AND
df.rubric_field_id = rf_first.first_img_id
)
WHERE d.document_meta_keywords LIKE '" . addslashes($keywords) . "%'
AND d.Id != 1
AND d.Id != '" . (int)PAGE_NOT_FOUND_ID . "'
AND d.Id != '" . (int)$AVE_Core->curentdoc->Id . "'
AND d.document_status != '0'
AND d.document_deleted != '1'
" . $inrubric . "
" . $doctime . "
GROUP BY d.Id
ORDER BY d.document_count_view DESC
LIMIT " . (int)$limit . "
");
while ($row = $sql->FetchRow())
{
if ($doctime != '' && (time() + $AVE_Template->cache_lifetime) > $row->document_expire)
{
// Изменяем время жизни кеша так что-бы оно не превышало
// время окончания публикации попавших в выборку документов
$AVE_Template->cache_lifetime = $row->document_expire - time();
}
$row->document_link = rewrite_link('index.php?id=' . $row->Id . '&amp;doc=' . (empty($row->document_alias) ? prepare_url($row->document_title) : $row->document_alias));
array_push($moredoc, $row);
}
// Закрываем соединение
$sql->Close();
}
// Передаём переменную moredoc в шаблон
$AVE_Template->assign('moredoc', $moredoc);
$lang_file = BASE_DIR . '/modules/moredoc/lang/' . $_SESSION['user_language'] . '.txt';
$AVE_Template->config_load($lang_file, 'module');
}
// Выводим шаблон moredoc.tpl
$AVE_Template->display($tpl_dir . 'moredoc.tpl', $AVE_Core->curentdoc->Id);
while ($row = $sql->FetchRow())
{
// Обработка изображения: берем кроп-миниатюру через ядро
if (!empty($row->document_image)) {
$img_parts = explode('|', $row->document_image);
$original_link = trim($img_parts[0]);
// 'c300x180' — жесткая обрезка под размер контейнера
$row->document_image = make_thumbnail(array(
'link' => $original_link,
'size' => 'c300x180'
));
}
$AVE_Template->caching = false; // Отключаем кеширование
if ($doctime != '' && (time() + $AVE_Template->cache_lifetime) > $row->document_expire)
{
$AVE_Template->cache_lifetime = $row->document_expire - time();
}
$row->document_link = rewrite_link('index.php?id=' . $row->Id . '&amp;doc=' . (empty($row->document_alias) ? prepare_url($row->document_title) : $row->document_alias));
array_push($moredoc, $row);
}
$sql->Close();
}
$AVE_Template->assign('moredoc', $moredoc);
$lang_file = BASE_DIR . '/modules/moredoc/lang/' . $_SESSION['user_language'] . '.txt';
$AVE_Template->config_load($lang_file, 'module');
}
$AVE_Template->display($tpl_dir . 'moredoc.tpl', $AVE_Core->curentdoc->Id);
$AVE_Template->caching = false;
}
?>
?>

View File

@@ -1,9 +1,452 @@
{*ВАРИАНТ ВЫВОДА КАРТОЧКИ 1*}
{if $moredoc}
<h3>{#MOREDOC_NAME#}</h3>
<ul>
{foreach from=$moredoc item=document}
<li><a href = "{$document->document_link}">{$document->document_title|escape}</a><br />
{if $document->document_meta_description !=''}{$document->document_meta_description|escape}<br />{/if}</li>
{/foreach}
</ul>
{/if}
<div class="moredoc-container my-5 px-3 px-md-0">
<div class="d-flex align-items-center mb-4 border-bottom pb-3">
<div class="bg-primary rounded-pill p-2 me-3 d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-grid text-white fs-5"></i>
</div>
<h3 class="fw-bolder mb-0 text-dark" style="letter-spacing: -0.5px;">{#MOREDOC_NAME#}</h3>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{foreach from=$moredoc item=document}
<div class="col">
<article class="card h-100 shadow-sm border-0 position-relative transition-hover overflow-hidden rounded-4">
{if isset($document->document_image)}
<div class="card-img-wrapper position-relative overflow-hidden" style="height: 180px; background-color: #f8f9fa;">
{if $document->document_image != ""}
<img src="{$document->document_image}" class="card-img-top w-100 h-100 object-fit-cover transition-zoom" alt="{$document->document_title|escape}">
{else}
<div class="w-100 h-100 d-flex flex-column align-items-center justify-content-center"
style="background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);">
<div class="text-center">
<div class="bg-white rounded-circle shadow-sm d-inline-flex align-items-center justify-content-center mb-2"
style="width: 50px; height: 50px;">
<i class="bi bi-image text-primary opacity-50 fs-3"></i>
</div>
<p class="small text-muted mb-0 fw-medium opacity-75" style="font-size: 0.7rem;">{#MOREDOC_NOIMAGE#}</p>
</div>
</div>
{/if}
<div class="position-absolute top-0 end-0 m-3">
<div class="badge bg-white bg-opacity-75 text-dark d-flex align-items-center px-2 py-1 rounded-pill shadow-sm small">
<i class="bi bi-eye-fill me-1 text-primary"></i>
<span class="fw-bold">{$document->document_count_view|default:0}</span>
</div>
</div>
</div>
{/if}
<div class="card-body p-4 d-flex flex-column">
<div class="d-flex align-items-center justify-content-between mb-3">
<div class="small text-primary fw-medium bg-sky-light px-3 py-1 rounded-pill w-fit-content">
<i class="bi bi-calendar3 me-2"></i>
<span>{$document->document_published|date_format:"%d.%m.%Y"}</span>
</div>
{if !isset($document->document_image)}
<div class="small text-muted fw-bold d-flex align-items-center">
<i class="bi bi-eye-fill me-1 text-primary"></i>
<span>{$document->document_count_view|default:0}</span>
</div>
{/if}
</div>
<h5 class="card-title fw-bolder mb-3 text-dark fs-5 line-clamp-2" style="line-height: 1.3;">
<a href="{$document->document_link}" class="text-decoration-none text-dark stretched-link title-link">
{$document->document_title|escape}
</a>
</h5>
{if $document->document_meta_description != ''}
<p class="card-text text-secondary small line-clamp-3 mb-0" style="line-height: 1.6;">
{$document->document_meta_description|strip_tags|truncate:140:"..."|escape}
</p>
{/if}
<div class="mt-auto pt-4 border-top">
<div class="d-flex justify-content-between align-items-center pt-2">
<span class="text-primary small fw-bold text-uppercase" style="font-size: 0.75rem; letter-spacing: 1px;">{#MOREDOC_READMORE#}</span>
<div class="btn-arrow-circle">
<i class="bi bi-arrow-right-short text-primary"></i>
</div>
</div>
</div>
</div>
</article>
</div>
{/foreach}
</div>
</div>
<style>
:root { --bs-primary: #0d6efd; --bg-sky-light: #eef4ff; }
.w-fit-content { width: fit-content; }
.bg-sky-light { background-color: var(--bg-sky-light); }
.object-fit-cover { object-fit: cover; }
.line-clamp-2, .line-clamp-3 { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.line-clamp-2 { -webkit-line-clamp: 2; }
.line-clamp-3 { -webkit-line-clamp: 3; }
.transition-hover { transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1); border: 1px solid rgba(0,0,0,0.05) !important; }
.transition-hover:hover { transform: translateY(-8px); box-shadow: 0 15px 30px rgba(0,0,0,0.1) !important; }
.transition-zoom { transition: transform 0.6s ease; }
.transition-hover:hover .transition-zoom { transform: scale(1.1); }
.transition-hover:hover .title-link { color: var(--bs-primary) !important; }
.btn-arrow-circle { width: 32px; height: 32px; background: #f1f3f5; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: 0.3s; }
.transition-hover:hover .btn-arrow-circle { background: var(--bs-primary); }
.transition-hover:hover .btn-arrow-circle i { color: #fff !important; }
</style>
{/if}
{*ВАРИАНТ ВЫВОДА КАРТОЧКИ 2*}
{* ---- начало комментария
{if $moredoc}
<div class="moredoc-container my-5 px-3 px-md-0">
<div class="d-flex align-items-center mb-4 border-bottom pb-3">
<div class="bg-primary rounded-pill p-2 me-3 d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-collection-play text-white fs-5"></i>
</div>
<h3 class="fw-bolder mb-0 text-dark" style="letter-spacing: -0.5px;">{#MOREDOC_NAME#}</h3>
</div>
<div class="row g-4">
{foreach from=$moredoc item=document}
<div class="col-12 col-xl-6">
<article class="card h-100 shadow-sm border-0 overflow-hidden rounded-4 transition-hover">
<div class="row g-0 h-100">
{if isset($document->document_image)}
<div class="col-sm-4 bg-light position-relative" style="min-height: 180px;">
{if $document->document_image != ""}
<img src="{$document->document_image}" class="w-100 h-100 object-fit-cover" alt="{$document->document_title|escape}">
{else}
<div class="w-100 h-100 d-flex align-items-center justify-content-center bg-light border-end">
<i class="bi bi-image text-muted opacity-25 fs-1"></i>
</div>
{/if}
<div class="position-absolute top-0 start-0 m-2">
<span class="badge bg-dark bg-opacity-50 rounded-pill small">
<i class="bi bi-eye me-1"></i>{$document->document_count_view|default:0}
</span>
</div>
</div>
{/if}
<div class="{if isset($document->document_image)}col-sm-8{else}col-12{/if}">
<div class="card-body p-4 d-flex flex-column h-100">
<div class="d-flex align-items-center justify-content-between mb-2">
<div class="small text-primary fw-bold">
<i class="bi bi-calendar3 me-1"></i> {$document->document_published|date_format:"%d.%m.%Y"}
</div>
{if !isset($document->document_image)}
<div class="small text-muted fw-bold d-flex align-items-center">
<i class="bi bi-eye-fill me-1 text-primary"></i>
<span>{$document->document_count_view|default:0}</span>
</div>
{/if}
</div>
<h5 class="fw-bold mb-2">
<a href="{$document->document_link}" class="text-decoration-none text-dark stretched-link">
{$document->document_title|escape}
</a>
</h5>
<p class="card-text text-secondary small line-clamp-2 mb-0">
{$document->document_meta_description|strip_tags|truncate:120:"..."|escape}
</p>
</div>
</div>
</div>
</article>
</div>
{/foreach}
</div>
</div>
<style>
.object-fit-cover { object-fit: cover; }
.line-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.transition-hover { transition: all 0.3s ease; border: 1px solid rgba(0,0,0,0.05) !important; }
.transition-hover:hover {
transform: translateX(5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.08) !important;
}
.transition-hover:hover .fw-bold a { color: var(--bs-primary) !important; }
</style>
{/if}
------ конец комментария*}
{*ВАРИАНТ ВЫВОДА КАРТОЧКИ 3*}
{* ---- начало комментария
{if $moredoc}
<div class="moredoc-container my-5 px-3 px-md-0">
<div class="d-flex justify-content-between align-items-end mb-4 shadow-sm pb-3 border-bottom">
<h3 class="fw-black mb-0 text-uppercase text-primary" style="letter-spacing: 1px; font-weight: 900;">
{#MOREDOC_NAME#}
</h3>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{foreach from=$moredoc item=document}
<div class="col">
<article class="card h-100 border-0 bg-light rounded-4 transition-card position-relative">
{if isset($document->document_image)}
<div class="position-relative p-2">
<div class="rounded-4 overflow-hidden shadow-sm" style="height: 190px;">
{if $document->document_image != ""}
<img src="{$document->document_image}" class="w-100 h-100 object-fit-cover transition-zoom" alt="{$document->document_title|escape}">
{else}
<div class="w-100 h-100 d-flex flex-column align-items-center justify-content-center bg-white">
<i class="bi bi-image-alt fs-1 text-primary opacity-25"></i>
<span class="small text-muted opacity-50 mt-1" style="font-size: 0.7rem;">{#MOREDOC_NOIMAGE#}</span>
</div>
{/if}
</div>
<div class="position-absolute top-0 end-0 m-3">
<div class="badge bg-dark bg-opacity-50 backdrop-blur d-flex align-items-center px-2 py-1 rounded-pill small">
<i class="bi bi-eye me-1 text-white"></i>
<span class="text-white">{$document->document_count_view|default:0}</span>
</div>
</div>
</div>
{/if}
<div class="card-body pt-2 px-4 pb-4 d-flex flex-column">
<div class="d-flex align-items-center justify-content-between mb-3">
<span class="badge bg-white text-primary shadow-sm px-3 py-2 rounded-pill small fw-bold">
<i class="bi bi-calendar3 me-1"></i> {$document->document_published|date_format:"%d.%m.%Y"}
</span>
{if !isset($document->document_image)}
<div class="small text-muted fw-bold d-flex align-items-center bg-white px-3 py-1 rounded-pill shadow-sm" style="height: 33px;">
<i class="bi bi-eye-fill me-1 text-primary"></i>
<span>{$document->document_count_view|default:0}</span>
</div>
{/if}
</div>
<h5 class="fw-bold mb-3 line-clamp-2">
<a href="{$document->document_link}" class="text-decoration-none text-dark stretched-link title-hover">
{$document->document_title|escape}
</a>
</h5>
{if $document->document_meta_description != ''}
<p class="card-text text-secondary small line-clamp-3 mb-4" style="line-height: 1.5;">
{$document->document_meta_description|strip_tags|truncate:140:"..."|escape}
</p>
{/if}
<div class="d-flex align-items-center justify-content-between mt-auto pt-3 border-top border-2 border-white">
<span class="small fw-bold text-uppercase text-muted" style="font-size: 0.75rem; letter-spacing: 0.5px;">{#MOREDOC_READMORE#}</span>
<div class="btn-arrow-circle-mini">
<i class="bi bi-arrow-right-short text-primary fs-4"></i>
</div>
</div>
</div>
</article>
</div>
{/foreach}
</div>
</div>
<style>
.object-fit-cover { object-fit: cover; }
.line-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.line-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.backdrop-blur { backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.transition-card {
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
border: 1px solid transparent !important;
}
.transition-card:hover {
background-color: #ffffff !important;
box-shadow: 0 15px 35px rgba(0,0,0,0.1) !important;
transform: translateY(-5px);
border-color: #eee !important;
}
.title-hover { transition: color 0.2s ease; }
.transition-card:hover .title-hover { color: #0d6efd !important; }
.transition-zoom { transition: transform 0.6s ease; }
.transition-card:hover .transition-zoom { transform: scale(1.05); }
.btn-arrow-circle-mini {
width: 36px; height: 36px; background: #fff; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
transition: all 0.3s ease; box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.transition-card:hover .btn-arrow-circle-mini {
background: #0d6efd; transform: rotate(-45deg);
}
.transition-card:hover .btn-arrow-circle-mini i { color: #fff !important; }
</style>
{/if}
------ конец комментария*}
{*ВАРИАНТ ВЫВОДА КАРТОЧКИ 4*}
{* ---- начало комментария
{if $moredoc}
<div class="moredoc-container my-5 px-3 px-md-0">
<div class="d-flex align-items-center mb-5 pb-3 border-bottom border-2">
<div class="vr bg-primary opacity-100 me-3" style="width: 4px; height: 30px; border-radius: 2px;"></div>
<h3 class="fw-bold mb-0 text-dark text-uppercase" style="letter-spacing: 1px;">{#MOREDOC_NAME#}</h3>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{foreach from=$moredoc item=document}
<div class="col">
<article class="card h-100 border-0 bg-light rounded-4 transition-card">
{if isset($document->document_image)}
<div class="position-relative p-2">
<div class="rounded-4 overflow-hidden shadow-sm" style="height: 200px; background-color: #dee2e6;">
{if $document->document_image != ""}
<img src="{$document->document_image}" class="card-img-top w-100 h-100 object-fit-cover transition-zoom" alt="{$document->document_title|escape}">
{else}
<div class="w-100 h-100 d-flex flex-column align-items-center justify-content-center bg-white opacity-75">
<i class="bi bi-image text-primary opacity-25" style="font-size: 3rem;"></i>
<span class="small text-muted mt-2">{#MOREDOC_NOIMAGE#}</span>
</div>
{/if}
</div>
<div class="position-absolute top-0 end-0 m-3">
<div class="badge bg-dark bg-opacity-50 backdrop-blur d-flex align-items-center px-3 py-2 rounded-pill small border border-white border-opacity-25">
<i class="bi bi-eye-fill me-2 text-white"></i>
<span class="fw-medium text-white">{$document->document_count_view|default:0}</span>
</div>
</div>
</div>
{/if}
<div class="card-body px-4 pb-4 pt-3 d-flex flex-column">
<div class="d-flex align-items-center justify-content-between mb-3">
<div class="small text-muted fw-medium d-flex align-items-center">
<i class="bi bi-calendar-event me-2 text-primary"></i>
{$document->document_published|date_format:"%d.%m.%Y"}
</div>
{if !isset($document->document_image)}
<div class="small text-muted fw-bold d-flex align-items-center">
<i class="bi bi-eye-fill me-1 text-primary"></i>
<span>{$document->document_count_view|default:0}</span>
</div>
{/if}
</div>
<h5 class="card-title fw-bold mb-3">
<a href="{$document->document_link}" class="text-decoration-none text-dark stretched-link title-link-effect" style="line-height: 1.4;">
{$document->document_title|escape}
</a>
</h5>
{if $document->document_meta_description != ''}
<p class="card-text text-secondary small mb-4 line-clamp-3" style="line-height: 1.6; opacity: 0.8;">
{$document->document_meta_description|strip_tags|truncate:140:"..."|escape}
</p>
{/if}
<div class="mt-auto pt-3 border-top border-white border-2 d-flex align-items-center justify-content-between">
<span class="text-uppercase fw-bold text-primary" style="font-size: 0.7rem; letter-spacing: 1px;">{#MOREDOC_READMORE#}</span>
<div class="btn-arrow-wrapper">
<div class="btn-circle-styled">
<i class="bi bi-arrow-right-short text-primary fs-4"></i>
</div>
</div>
</div>
</div>
</article>
</div>
{/foreach}
</div>
</div>
<style>
.object-fit-cover { object-fit: cover; }
.line-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.backdrop-blur { backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.transition-card {
transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
outline: 1px solid rgba(0,0,0,0.02);
}
.transition-card:hover {
background-color: #ffffff !important;
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0,0,0,0.08) !important;
outline: 1px solid rgba(0,0,0,0.05);
}
.transition-zoom { transition: transform 0.8s ease; }
.transition-card:hover .transition-zoom { transform: scale(1.08); }
.title-link-effect { transition: color 0.3s ease; }
.transition-card:hover .title-link-effect { color: #0d6efd !important; }
.btn-circle-styled {
width: 40px; height: 40px; background-color: #ffffff;
border-radius: 50%; display: flex; align-items: center; justify-content: center;
transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.transition-card:hover .btn-circle-styled {
background-color: #0d6efd;
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.3);
}
.transition-card:hover .btn-circle-styled i { color: #ffffff !important; }
.btn-arrow-wrapper { transition: transform 0.3s ease; }
.transition-card:hover .btn-arrow-wrapper { transform: rotate(-45deg); }
</style>
{/if}
------ конец комментария*}