From a330d5dc83f38a9c335407f727fbbb526c49f09e Mon Sep 17 00:00:00 2001 From: Repellent Date: Mon, 30 Mar 2026 22:38:23 +0500 Subject: [PATCH] update ver 3.31 --- README.md | 22 +- info.php | 2 +- lang/ru.txt | 4 +- module.php | 188 +++++++++-------- templates/moredoc.tpl | 459 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 584 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 928caa2..31ad4c6 100644 --- a/README.md +++ b/README.md @@ -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.

Для вывода списка похожих документов используйте системный тег [mod_moredoc] (можно использовать как в документах так и шаблоне рубрики). + * Важно! После каждого действия имеющего отношение к модулю обязательно чистите кеш системы иначе вы не увидите результатов - модуль жестко берет данные из кеша если кеш модуля существует. +### Список похожих документов начиная с версии модуля 3.31 выводится карточкой, верстка Bootstrap 5 + * В карточке отображаются следующие параметры документа: + * Название документа берется из ```HTML ``` + * Описание документа берется из ```(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 \ No newline at end of file diff --git a/info.php b/info.php index 829047a..4e73b8a 100644 --- a/info.php +++ b/info.php @@ -4,7 +4,7 @@ $module = array( 'ModuleSysName' => 'moredoc', - 'ModuleVersion' => '1.26.0', + 'ModuleVersion' => '3.31', 'ModuleAutor' => 'AVE.cms Team', 'ModuleCopyright' => '© 2007-' . date('Y') . ' AVE.cms', 'ModuleStatus' => 1, diff --git a/lang/ru.txt b/lang/ru.txt index b7a22ba..a54eac3 100644 --- a/lang/ru.txt +++ b/lang/ru.txt @@ -3,4 +3,6 @@ MODULE_NAME = "Ссылки по теме" MODULE_DESCRIPTION = "Данный модуль предназначен для вывода списка похожих документов относительно текущего. Связующим элементом документов является первое слово из поля Ключевые слова. Результат вывода кешируется средствами Smarty.<BR /><BR />Для вывода списка похожих документов используйте системный тег <strong>[mod_moredoc]</strong> (можно использовать как в документах так и шаблоне рубрики)." [module] -MOREDOC_NAME = "Возможно Вас заинтересует:" \ No newline at end of file +MOREDOC_NAME = "Возможно Вас заинтересует:" +MOREDOC_NOIMAGE = "Нет изображения" +MOREDOC_READMORE = "Подробнее" \ No newline at end of file diff --git a/module.php b/module.php index 9335808..a566c5a 100644 --- a/module.php +++ b/module.php @@ -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 . '&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 . '&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; } - -?> +?> \ No newline at end of file diff --git a/templates/moredoc.tpl b/templates/moredoc.tpl index 17a8735..66ee597 100644 --- a/templates/moredoc.tpl +++ b/templates/moredoc.tpl @@ -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} \ No newline at end of file +<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} + + + ------ конец комментария*} \ No newline at end of file