Add new module
This commit is contained in:
BIN
modules/.DS_Store
vendored
Normal file
BIN
modules/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
modules/appagebuilder/classes/.DS_Store
vendored
Normal file
BIN
modules/appagebuilder/classes/.DS_Store
vendored
Normal file
Binary file not shown.
37
modules/customdevslider/classes/CustomDevSliderSlide.php
Normal file
37
modules/customdevslider/classes/CustomDevSliderSlide.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class CustomDevSliderSlide extends ObjectModel
|
||||||
|
{
|
||||||
|
public $id_customdevslider_slide;
|
||||||
|
|
||||||
|
public $image;
|
||||||
|
public $link;
|
||||||
|
public $position;
|
||||||
|
public $active;
|
||||||
|
|
||||||
|
public $title; // multilang
|
||||||
|
public $text; // multilang
|
||||||
|
|
||||||
|
public static $definition = [
|
||||||
|
'table' => 'customdevslider_slide',
|
||||||
|
'primary' => 'id_customdevslider_slide',
|
||||||
|
'multilang' => true,
|
||||||
|
'fields' => [
|
||||||
|
'image' => ['type' => self::TYPE_STRING, 'validate' => 'isFileName', 'size' => 255],
|
||||||
|
'link' => ['type' => self::TYPE_STRING, 'validate' => 'isUrlOrEmpty', 'size' => 255],
|
||||||
|
'position' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||||
|
'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||||
|
|
||||||
|
'title' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCleanHtml', 'size' => 255],
|
||||||
|
'text' => ['type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function isUrlOrEmpty($url)
|
||||||
|
{
|
||||||
|
if ($url === null || $url === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Validate::isUrl($url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once _PS_MODULE_DIR_.'customdevslider/classes/CustomDevSliderSlide.php';
|
||||||
|
|
||||||
|
class AdminCustomDevSliderController extends ModuleAdminController
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->bootstrap = true;
|
||||||
|
|
||||||
|
$this->table = 'customdevslider_slide';
|
||||||
|
$this->className = 'CustomDevSliderSlide';
|
||||||
|
$this->identifier = 'id_customdevslider_slide';
|
||||||
|
$this->lang = true;
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->_defaultOrderBy = 'position';
|
||||||
|
$this->_defaultOrderWay = 'ASC';
|
||||||
|
|
||||||
|
$this->fields_list = [
|
||||||
|
'id_customdevslider_slide' => ['title' => 'ID', 'class' => 'fixed-width-xs'],
|
||||||
|
'title' => ['title' => $this->l('Title')],
|
||||||
|
'link' => ['title' => $this->l('Link')],
|
||||||
|
'position' => ['title' => $this->l('Position'), 'class' => 'fixed-width-sm'],
|
||||||
|
'active' => ['title' => $this->l('Active'), 'type' => 'bool', 'active' => 'status', 'class' => 'fixed-width-sm'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->addRowAction('edit');
|
||||||
|
$this->addRowAction('delete');
|
||||||
|
|
||||||
|
// Кнопка "Додати"
|
||||||
|
$this->bulk_actions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderForm()
|
||||||
|
{
|
||||||
|
/** @var CustomDevSliderSlide $obj */
|
||||||
|
$obj = $this->object;
|
||||||
|
|
||||||
|
$imagePreview = '';
|
||||||
|
if ($obj && !empty($obj->image)) {
|
||||||
|
$imageUrl = __PS_BASE_URI__.'modules/customdevslider/uploads/'.$obj->image;
|
||||||
|
$imagePreview = '<div style="margin-top:10px;"><img src="'.$imageUrl.'" style="max-width:220px;height:auto;border:1px solid #ddd;padding:5px;"></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fields_form = [
|
||||||
|
'legend' => ['title' => $this->l('Slide')],
|
||||||
|
'input' => [
|
||||||
|
[
|
||||||
|
'type' => 'file',
|
||||||
|
'label' => $this->l('Image'),
|
||||||
|
'name' => 'image_file',
|
||||||
|
'desc' => $this->l('Allowed: jpg, jpeg, png, webp, gif.').$imagePreview,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'text',
|
||||||
|
'label' => $this->l('Link'),
|
||||||
|
'name' => 'link',
|
||||||
|
'desc' => $this->l('Optional URL (https://...)'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'text',
|
||||||
|
'label' => $this->l('Title'),
|
||||||
|
'name' => 'title',
|
||||||
|
'lang' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'textarea',
|
||||||
|
'label' => $this->l('Text'),
|
||||||
|
'name' => 'text',
|
||||||
|
'lang' => true,
|
||||||
|
'autoload_rte' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'text',
|
||||||
|
'label' => $this->l('Position'),
|
||||||
|
'name' => 'position',
|
||||||
|
'class' => 'fixed-width-sm',
|
||||||
|
'desc' => $this->l('Sorting order (0..n). Lower goes first.'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'switch',
|
||||||
|
'label' => $this->l('Active'),
|
||||||
|
'name' => 'active',
|
||||||
|
'is_bool' => true,
|
||||||
|
'values' => [
|
||||||
|
['id' => 'active_on', 'value' => 1, 'label' => $this->l('Enabled')],
|
||||||
|
['id' => 'active_off', 'value' => 0, 'label' => $this->l('Disabled')],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'submit' => ['title' => $this->l('Save')],
|
||||||
|
];
|
||||||
|
|
||||||
|
return parent::renderForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processAdd()
|
||||||
|
{
|
||||||
|
$this->handleImageUpload();
|
||||||
|
return parent::processAdd();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processUpdate()
|
||||||
|
{
|
||||||
|
$this->handleImageUpload();
|
||||||
|
return parent::processUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleImageUpload()
|
||||||
|
{
|
||||||
|
if (!isset($_FILES['image_file']) || empty($_FILES['image_file']['name'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $_FILES['image_file'];
|
||||||
|
|
||||||
|
if (!empty($file['error'])) {
|
||||||
|
$this->errors[] = $this->l('Image upload error.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ext = Tools::strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||||
|
$allowed = ['jpg', 'jpeg', 'png', 'webp', 'gif'];
|
||||||
|
|
||||||
|
if (!in_array($ext, $allowed, true)) {
|
||||||
|
$this->errors[] = $this->l('Invalid image format.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uploadDir = _PS_MODULE_DIR_.'customdevslider/uploads/';
|
||||||
|
if (!is_dir($uploadDir)) {
|
||||||
|
if (!@mkdir($uploadDir, 0755, true)) {
|
||||||
|
$this->errors[] = $this->l('Cannot create uploads directory.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Нормальне безпечне ім'я
|
||||||
|
$safeName = sha1(uniqid('customdevslider_', true)).'.'.$ext;
|
||||||
|
$dest = $uploadDir.$safeName;
|
||||||
|
|
||||||
|
if (!move_uploaded_file($file['tmp_name'], $dest)) {
|
||||||
|
$this->errors[] = $this->l('Image upload failed.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Записуємо ім'я файлу в поле image
|
||||||
|
$_POST['image'] = $safeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
modules/customdevslider/css/front.css
Normal file
1
modules/customdevslider/css/front.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.hp-c-slider{padding:0 !important}.hp-c-slider>.ApColumn{padding:0 !important}div[id^=customdevslider-] .customdevslider-swiper__container{position:relative}div[id^=customdevslider-] .customdevslider-swiper__container .swiper-wrapper{position:absolute;inset:0}div[id^=customdevslider-] .customdevslider-swiper__container .swiper-wrapper .swiper-slide .customdevslider-swiper__image{height:100%;-o-object-fit:cover;object-fit:cover;-o-object-position:left center;object-position:left center}div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content{position:relative;margin:50px 0 50px auto;row-gap:40px;min-height:548px;width:50%;height:100%;background:rgba(120,120,120,.65);padding:24px;color:#f2f2f2;z-index:1;display:flex;flex-direction:column}@media(max-width: 1300px){div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content{width:60%}}@media(max-width: 900px){div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content{width:100%;margin:0;padding:50px;background-color:rgba(34,34,34,.85);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}}div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content .customdevslider-swiper__text{flex:1 1 auto;overflow:hidden;font-size:20px;line-height:1.3}div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content .customdevslider-swiper__pagination{display:flex;flex-direction:row;gap:20px;justify-content:space-between;flex:0 0 auto;margin-top:auto;position:static !important;text-align:center}@media(max-width: 700px){div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content .customdevslider-swiper__pagination{display:flex;flex-direction:row;flex-wrap:wrap}}div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content .customdevslider-swiper__pagination .swiper-pagination-bullet{width:auto;height:auto;border-radius:0;background:rgba(0,0,0,0);opacity:1;margin:0 !important;padding:0;color:#fff;font-size:26px;letter-spacing:1px;text-transform:uppercase;font-weight:500;display:inline-block;cursor:pointer;min-width:-moz-max-content;min-width:max-content}@media(max-width: 700px){div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content .customdevslider-swiper__pagination .swiper-pagination-bullet{flex:40%}}@media(max-width: 460px){div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content .customdevslider-swiper__pagination .swiper-pagination-bullet{font-size:18px}}div[id^=customdevslider-] .customdevslider-swiper__container .customdevslider-swiper__content .customdevslider-swiper__pagination .swiper-pagination-bullet.swiper-pagination-bullet-active{color:#e60000}/*# sourceMappingURL=front.css.map */
|
||||||
1
modules/customdevslider/css/front.css.map
Normal file
1
modules/customdevslider/css/front.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["front.scss"],"names":[],"mappings":"AAAA,aACC,oBAAA,CAEA,uBACC,oBAAA,CAID,6DACC,iBAAA,CAEA,6EACC,iBAAA,CACA,OAAA,CAGC,0HACC,WAAA,CACA,mBAAA,CAAA,gBAAA,CACA,8BAAA,CAAA,2BAAA,CAIH,8FACC,iBAAA,CACA,uBAAA,CACA,YAAA,CACA,gBAAA,CAKA,SAAA,CAEA,WAAA,CACA,gCAAA,CACA,YAAA,CACA,aAAA,CACA,SAAA,CAEA,YAAA,CACA,qBAAA,CAEA,0BApBD,8FAqBE,SAAA,CAAA,CAGD,yBAxBD,8FAyBE,UAAA,CACA,QAAA,CACA,YAAA,CACA,mCAAA,CACA,iCAAA,CAAA,yBAAA,CAAA,CAGD,4HACC,aAAA,CACA,eAAA,CACA,cAAA,CACA,eAAA,CAGD,kIACC,YAAA,CACA,kBAAA,CACA,QAAA,CACA,6BAAA,CAEA,aAAA,CACA,eAAA,CACA,0BAAA,CACA,iBAAA,CAEA,yBAXD,kIAYE,YAAA,CACA,kBAAA,CACA,cAAA,CAAA,CAGD,4JACC,UAAA,CACA,WAAA,CACA,eAAA,CACA,wBAAA,CACA,SAAA,CACA,mBAAA,CACA,SAAA,CACA,UAAA,CACA,cAAA,CACA,kBAAA,CACA,wBAAA,CACA,eAAA,CACA,oBAAA,CACA,cAAA,CAEA,0BAAA,CAAA,qBAAA,CAEA,yBAlBD,4JAmBE,QAAA,CAAA,CAED,yBArBD,4JAsBE,cAAA,CAAA,CAED,4LACC,aAAA","file":"front.css"}
|
||||||
111
modules/customdevslider/css/front.scss
Normal file
111
modules/customdevslider/css/front.scss
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
.hp-c-slider {
|
||||||
|
padding: 0 !important;
|
||||||
|
|
||||||
|
> .ApColumn {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div[id^='customdevslider-'] {
|
||||||
|
.customdevslider-swiper__container {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.swiper-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
|
||||||
|
.swiper-slide {
|
||||||
|
.customdevslider-swiper__image {
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: left center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.customdevslider-swiper__content {
|
||||||
|
position: relative;
|
||||||
|
margin: 50px 0 50px auto;
|
||||||
|
row-gap: 40px;
|
||||||
|
min-height: 548px;
|
||||||
|
|
||||||
|
// position: absolute;
|
||||||
|
// right: 5%;
|
||||||
|
// top: 10%;
|
||||||
|
width: 50%;
|
||||||
|
// height: 80%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(120, 120, 120, 0.65);
|
||||||
|
padding: 24px;
|
||||||
|
color: #f2f2f2;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
@media (max-width: 1300px) {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 50px;
|
||||||
|
background-color: rgb(34 34 34 / 85%);
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customdevslider-swiper__text {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customdevslider-swiper__pagination {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 20px;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-top: auto;
|
||||||
|
position: static !important;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-pagination-bullet {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
opacity: 1;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 26px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
min-width: max-content;
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
flex: 40%;
|
||||||
|
}
|
||||||
|
@media (max-width: 460px) {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
&.swiper-pagination-bullet-active {
|
||||||
|
color: #e60000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
195
modules/customdevslider/customdevslider.php
Normal file
195
modules/customdevslider/customdevslider.php
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<?php
|
||||||
|
if (!defined('_PS_VERSION_')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomDevSlider extends Module implements \PrestaShop\PrestaShop\Core\Module\WidgetInterface
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->name = 'customdevslider';
|
||||||
|
$this->tab = 'front_office_features';
|
||||||
|
$this->version = '1.0.0';
|
||||||
|
$this->author = 'CustomDev';
|
||||||
|
$this->need_instance = 0;
|
||||||
|
$this->bootstrap = true;
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->displayName = $this->l('Custom Dev Slider');
|
||||||
|
$this->description = $this->l('Custom slider with dynamic slides (image, title, text, link).');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function install()
|
||||||
|
{
|
||||||
|
return parent::install()
|
||||||
|
&& $this->installDb()
|
||||||
|
&& $this->installTab()
|
||||||
|
&& $this->registerHook('displayHome')
|
||||||
|
&& $this->registerHook('displayTop')
|
||||||
|
&& $this->registerHook('header');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uninstall()
|
||||||
|
{
|
||||||
|
return $this->uninstallTab()
|
||||||
|
&& $this->uninstallDb()
|
||||||
|
&& parent::uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hookDisplayTop($params)
|
||||||
|
{
|
||||||
|
return $this->renderWidget('displayTop', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hookDisplayHome($params)
|
||||||
|
{
|
||||||
|
return $this->renderWidget('displayHome', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
Tools::redirectAdmin($this->context->link->getAdminLink('AdminCustomDevSlider'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hookHeader()
|
||||||
|
{
|
||||||
|
// Swiper (локально)
|
||||||
|
$this->context->controller->registerStylesheet(
|
||||||
|
'customdevslider-swiper',
|
||||||
|
'modules/'.$this->name.'/plugins/swiperjs/swiper.min.css',
|
||||||
|
['media' => 'all', 'priority' => 140]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Твій CSS (після swiper)
|
||||||
|
$this->context->controller->registerStylesheet(
|
||||||
|
'customdevslider-front',
|
||||||
|
'modules/'.$this->name.'/css/front.css',
|
||||||
|
['media' => 'all', 'priority' => 150]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Swiper JS (перед твоїм JS)
|
||||||
|
$this->context->controller->registerJavascript(
|
||||||
|
'customdevslider-swiper',
|
||||||
|
'modules/'.$this->name.'/plugins/swiperjs/swiper.min.js',
|
||||||
|
['position' => 'bottom', 'priority' => 140]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Твій JS ініціалізації
|
||||||
|
$this->context->controller->registerJavascript(
|
||||||
|
'customdevslider-front',
|
||||||
|
'modules/'.$this->name.'/js/front.js',
|
||||||
|
['position' => 'bottom', 'priority' => 150]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// WidgetInterface
|
||||||
|
public function renderWidget($hookName, array $configuration)
|
||||||
|
{
|
||||||
|
$this->smarty->assign($this->getWidgetVariables($hookName, $configuration));
|
||||||
|
return $this->fetch('module:'.$this->name.'/views/templates/hook/slider.tpl');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWidgetVariables($hookName, array $configuration)
|
||||||
|
{
|
||||||
|
$idLang = (int)$this->context->language->id;
|
||||||
|
|
||||||
|
$slides = Db::getInstance()->executeS('
|
||||||
|
SELECT s.*, sl.title, sl.text
|
||||||
|
FROM `'._DB_PREFIX_.'customdevslider_slide` s
|
||||||
|
LEFT JOIN `'._DB_PREFIX_.'customdevslider_slide_lang` sl
|
||||||
|
ON (s.id_customdevslider_slide = sl.id_customdevslider_slide AND sl.id_lang = '.$idLang.')
|
||||||
|
WHERE s.active = 1
|
||||||
|
ORDER BY s.position ASC
|
||||||
|
');
|
||||||
|
|
||||||
|
foreach ($slides as &$s) {
|
||||||
|
$s['image_url'] = $s['image']
|
||||||
|
? $this->context->link->getMediaLink(_MODULE_DIR_.$this->name.'/uploads/'.$s['image'])
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
unset($s);
|
||||||
|
|
||||||
|
// UID щоб кілька віджетів не конфліктували
|
||||||
|
$uid = Tools::passwdGen(10, 'NO_NUMERIC');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'customdevslider_slides' => $slides,
|
||||||
|
'customdevslider_uid' => $uid,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function installDb()
|
||||||
|
{
|
||||||
|
$sql = [];
|
||||||
|
|
||||||
|
$sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'customdevslider_slide` (
|
||||||
|
`id_customdevslider_slide` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`image` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`link` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`position` INT UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
`active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
`date_add` DATETIME NULL,
|
||||||
|
`date_upd` DATETIME NULL,
|
||||||
|
PRIMARY KEY (`id_customdevslider_slide`)
|
||||||
|
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;';
|
||||||
|
|
||||||
|
$sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'customdevslider_slide_lang` (
|
||||||
|
`id_customdevslider_slide` INT UNSIGNED NOT NULL,
|
||||||
|
`id_lang` INT UNSIGNED NOT NULL,
|
||||||
|
`title` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`text` TEXT DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id_customdevslider_slide`, `id_lang`)
|
||||||
|
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;';
|
||||||
|
|
||||||
|
foreach ($sql as $q) {
|
||||||
|
if (!Db::getInstance()->execute($q)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function uninstallDb()
|
||||||
|
{
|
||||||
|
$sql = [];
|
||||||
|
$sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'customdevslider_slide_lang`';
|
||||||
|
$sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'customdevslider_slide`';
|
||||||
|
|
||||||
|
foreach ($sql as $q) {
|
||||||
|
if (!Db::getInstance()->execute($q)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function installTab()
|
||||||
|
{
|
||||||
|
$idParent = (int)Tab::getIdFromClassName('AdminParentModulesSf');
|
||||||
|
|
||||||
|
$tab = new Tab();
|
||||||
|
$tab->active = 1;
|
||||||
|
$tab->class_name = 'AdminCustomDevSlider';
|
||||||
|
$tab->module = $this->name;
|
||||||
|
$tab->id_parent = $idParent;
|
||||||
|
|
||||||
|
$tab->name = [];
|
||||||
|
foreach (Language::getLanguages(true) as $lang) {
|
||||||
|
$tab->name[(int)$lang['id_lang']] = 'Custom Dev Slider';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool)$tab->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function uninstallTab()
|
||||||
|
{
|
||||||
|
$idTab = (int)Tab::getIdFromClassName('AdminCustomDevSlider');
|
||||||
|
if ($idTab) {
|
||||||
|
$tab = new Tab($idTab);
|
||||||
|
return (bool)$tab->delete();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
98
modules/customdevslider/js/front.js
Normal file
98
modules/customdevslider/js/front.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
$(function () {
|
||||||
|
var $roots = $('.customdevslider-swiper')
|
||||||
|
if (!$roots.length) return
|
||||||
|
|
||||||
|
$roots.each(function () {
|
||||||
|
var $root = $(this)
|
||||||
|
|
||||||
|
if ($root.data('inited') === 1) return
|
||||||
|
$root.data('inited', 1)
|
||||||
|
|
||||||
|
var $container = $root.find('.customdevslider-swiper__container').first()
|
||||||
|
var $paginationEl = $root
|
||||||
|
.find('.customdevslider-swiper__pagination')
|
||||||
|
.first()
|
||||||
|
var $textBox = $root.find('.customdevslider-swiper__text').first()
|
||||||
|
|
||||||
|
if (
|
||||||
|
!$container.length ||
|
||||||
|
!$paginationEl.length ||
|
||||||
|
typeof Swiper === 'undefined'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
var titles = []
|
||||||
|
try {
|
||||||
|
titles = JSON.parse($root.attr('data-titles') || '[]')
|
||||||
|
} catch (e) {
|
||||||
|
titles = []
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHtmlByIndex(idx) {
|
||||||
|
var $slide = $container.find('.swiper-slide').eq(idx)
|
||||||
|
var html =
|
||||||
|
$slide.find('.customdevslider-swiper__slide-html').first().html() || ''
|
||||||
|
$textBox.html(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
var swiper = new Swiper($container[0], {
|
||||||
|
loop: true,
|
||||||
|
speed: 600,
|
||||||
|
autoplay: {
|
||||||
|
delay: 5000,
|
||||||
|
},
|
||||||
|
|
||||||
|
// fade
|
||||||
|
effect: 'fade',
|
||||||
|
fadeEffect: { crossFade: true },
|
||||||
|
|
||||||
|
pagination: {
|
||||||
|
el: $paginationEl[0],
|
||||||
|
clickable: true,
|
||||||
|
renderBullet: function (index, className) {
|
||||||
|
var t = (titles[index] || '').toString().trim()
|
||||||
|
if (!t) t = 'Slide ' + (index + 1)
|
||||||
|
return '<span class="' + className + '">' + t + '</span>'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
on: {
|
||||||
|
init: function () {
|
||||||
|
setMaxTextHeight()
|
||||||
|
setHtmlByIndex(this.realIndex)
|
||||||
|
},
|
||||||
|
slideChange: function () {
|
||||||
|
setHtmlByIndex(this.realIndex)
|
||||||
|
},
|
||||||
|
resize: function () {
|
||||||
|
setMaxTextHeight()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
setHtmlByIndex(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
function setMaxTextHeight() {
|
||||||
|
var maxHeight = 0
|
||||||
|
var originalHtml = $textBox.html()
|
||||||
|
|
||||||
|
$container.find('.swiper-slide').each(function () {
|
||||||
|
var html = $(this)
|
||||||
|
.find('.customdevslider-swiper__slide-html')
|
||||||
|
.first()
|
||||||
|
.html()
|
||||||
|
|
||||||
|
if (!html) return
|
||||||
|
|
||||||
|
$textBox.html(html)
|
||||||
|
$textBox.css('height', 'auto')
|
||||||
|
|
||||||
|
var h = $textBox.outerHeight(true)
|
||||||
|
if (h > maxHeight) maxHeight = h
|
||||||
|
})
|
||||||
|
|
||||||
|
$textBox.html(originalHtml)
|
||||||
|
$textBox.height(maxHeight)
|
||||||
|
}
|
||||||
|
})
|
||||||
BIN
modules/customdevslider/logo.png
Normal file
BIN
modules/customdevslider/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
13
modules/customdevslider/plugins/swiperjs/swiper.min.css
vendored
Normal file
13
modules/customdevslider/plugins/swiperjs/swiper.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7661
modules/customdevslider/plugins/swiperjs/swiper.min.js
vendored
Normal file
7661
modules/customdevslider/plugins/swiperjs/swiper.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
45
modules/customdevslider/views/templates/hook/slider.tpl
Normal file
45
modules/customdevslider/views/templates/hook/slider.tpl
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{if $customdevslider_slides|@count > 0}
|
||||||
|
{assign var="titles" value=[]}
|
||||||
|
{foreach from=$customdevslider_slides item=s}
|
||||||
|
{$titles[] = ($s.title|default:'')|strip_tags}
|
||||||
|
{/foreach}
|
||||||
|
|
||||||
|
<div class="customdevslider-swiper"
|
||||||
|
id="customdevslider-{$customdevslider_uid|escape:'htmlall':'UTF-8'}"
|
||||||
|
data-titles='{$titles|@json_encode|escape:'htmlall':'UTF-8'}'>
|
||||||
|
|
||||||
|
<div class="swiper customdevslider-swiper__container">
|
||||||
|
<div class="swiper-wrapper">
|
||||||
|
{foreach from=$customdevslider_slides item=s}
|
||||||
|
<div class="swiper-slide customdevslider-swiper__slide">
|
||||||
|
{if $s.link}
|
||||||
|
<a class="customdevslider-swiper__link" href="{$s.link|escape:'htmlall':'UTF-8'}">
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $s.image_url}
|
||||||
|
<img class="customdevslider-swiper__image"
|
||||||
|
src="{$s.image_url|escape:'htmlall':'UTF-8'}"
|
||||||
|
alt="{$s.title|escape:'htmlall':'UTF-8'}">
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $s.link}
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{* ВАЖЛИВО: зберігаємо HTML структуру тексту *}
|
||||||
|
<div class="customdevslider-swiper__slide-html" style="display:none;">
|
||||||
|
{$s.text nofilter}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/foreach}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{* ОДИН overlay content на весь слайдер *}
|
||||||
|
<div class="customdevslider-swiper__content">
|
||||||
|
<div class="customdevslider-swiper__text"></div>
|
||||||
|
<div class="swiper-pagination customdevslider-swiper__pagination"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
Reference in New Issue
Block a user