* @copyright 2024 Project-Pro */ if (!defined('_PS_VERSION_')) { exit; } require_once _PS_MODULE_DIR_ . 'projectproblog/classes/BlogPost.php'; require_once _PS_MODULE_DIR_ . 'projectproblog/classes/BlogCategory.php'; class AdminProjectproBlogPostsController extends ModuleAdminController { /** Katalog przechowywania miniaturek */ const IMG_DIR = 'projectproblog/views/img/posts/'; /** Flaga zapobiegająca podwójnemu wstrzyknięciu pól do formularza */ private $formEnriched = false; public function __construct() { $this->bootstrap = true; $this->table = 'projectproblog_post'; $this->className = 'BlogPost'; $this->lang = true; $this->identifier = 'id_post'; $this->_defaultOrderBy = 'date_add'; $this->_defaultOrderWay = 'DESC'; parent::__construct(); $this->addRowAction('edit'); $this->addRowAction('delete'); /* -------------------------------------------------------- */ /* Lista */ /* -------------------------------------------------------- */ $this->fields_list = [ 'id_post' => [ 'title' => $this->l('ID'), 'align' => 'center', 'class' => 'fixed-width-xs', ], 'title' => [ 'title' => $this->l('Tytuł'), 'filter_key' => 'b!title', ], 'date_add' => [ 'title' => $this->l('Data dodania'), 'type' => 'datetime', 'align' => 'center', ], 'active' => [ 'title' => $this->l('Aktywny'), 'active' => 'status', 'type' => 'bool', 'align' => 'center', 'class' => 'fixed-width-sm', ], ]; /* -------------------------------------------------------- */ /* Formularz — pola statyczne */ /* (miniaturka i kategorie dołączane dynamicznie */ /* w renderForm() żeby obsłużyć podgląd i zaznaczenia) */ /* -------------------------------------------------------- */ $this->fields_form = [ 'legend' => [ 'title' => $this->l('Wpis bloga'), 'icon' => 'icon-edit', ], 'input' => [ [ 'type' => 'switch', 'label' => $this->l('Aktywny'), 'name' => 'active', 'required' => false, 'is_bool' => true, 'values' => [ ['id' => 'active_on', 'value' => 1, 'label' => $this->l('Tak')], ['id' => 'active_off', 'value' => 0, 'label' => $this->l('Nie')], ], ], [ 'type' => 'text', 'label' => $this->l('Tytuł'), 'name' => 'title', 'lang' => true, 'required' => true, 'col' => 6, ], [ 'type' => 'text', 'label' => $this->l('Przyjazny URL (slug)'), 'name' => 'link_rewrite', 'lang' => true, 'required' => true, 'col' => 6, 'hint' => $this->l('Tylko małe litery, cyfry i myślniki. Generowany automatycznie z tytułu.'), ], [ 'type' => 'textarea', 'label' => $this->l('Wstęp'), 'name' => 'intro', 'lang' => true, 'rows' => 5, 'autoload_rte' => true, 'hint' => $this->l('Krótki opis wyświetlany na liście wpisów.'), ], [ 'type' => 'textarea', 'label' => $this->l('Treść'), 'name' => 'content', 'lang' => true, 'rows' => 20, 'autoload_rte' => true, ], [ 'type' => 'text', 'label' => $this->l('Meta title'), 'name' => 'meta_title', 'lang' => true, 'col' => 6, ], [ 'type' => 'text', 'label' => $this->l('Meta keywords'), 'name' => 'meta_keywords', 'lang' => true, 'col' => 6, 'hint' => $this->l('Słowa kluczowe oddzielone przecinkami.'), ], [ 'type' => 'textarea', 'label' => $this->l('Meta description'), 'name' => 'meta_description', 'lang' => true, 'rows' => 3, ], // miniaturka i kategorie → renderForm() ], 'submit' => [ 'title' => $this->l('Zapisz'), ], ]; } /* ------------------------------------------------------------------ */ /* Formularz — dynamiczne pola: miniaturka + kategorie */ /* ------------------------------------------------------------------ */ public function renderForm() { // Ochrona przed podwójnym wstrzyknięciem (np. przy re-renderze po błędzie walidacji) if ($this->formEnriched) { return parent::renderForm(); } $this->formEnriched = true; $idPost = (int) Tools::getValue('id_post'); $existingThumbnail = null; $selectedCategories = []; if ($idPost) { $post = new BlogPost($idPost); if (Validate::isLoadedObject($post)) { $existingThumbnail = $post->thumbnail; $selectedCategories = array_map('intval', BlogPost::getCategories($idPost)); } } // --- pole miniaturki --- $thumbnailHtml = '
'; $thumbnailHtml .= ''; $thumbnailHtml .= '
'; if ($existingThumbnail) { $url = Context::getContext()->link->getBaseLink() . 'modules/projectproblog/views/img/posts/' . $existingThumbnail; $thumbnailHtml .= '
' . '' . '
'; } $thumbnailHtml .= ''; $thumbnailHtml .= '

' . $this->l('Dozwolone formaty: jpg, png, webp, gif.') . '

'; $thumbnailHtml .= '
'; // --- pole kategorii --- $allCategories = Db::getInstance()->executeS( 'SELECT c.`id_category`, cl.`name` FROM `' . _DB_PREFIX_ . 'projectproblog_category` c LEFT JOIN `' . _DB_PREFIX_ . 'projectproblog_category_lang` cl ON c.`id_category` = cl.`id_category` AND cl.`id_lang` = ' . (int) $this->context->language->id . ' WHERE c.`active` = 1 ORDER BY cl.`name` ASC' ); $categoriesHtml = '
'; $categoriesHtml .= ''; $categoriesHtml .= '
'; if (!empty($allCategories)) { $categoriesHtml .= '
'; foreach ($allCategories as $cat) { $checked = in_array((int) $cat['id_category'], $selectedCategories) ? ' checked' : ''; $categoriesHtml .= '
' . '' . '
'; } $categoriesHtml .= '
'; } else { $categoriesHtml .= '

' . $this->l('Brak aktywnych kategorii. Dodaj kategorie w zakładce "Kategorie bloga".') . '

'; } $categoriesHtml .= '
'; // --- wstrzykiwanie do formularza --- $this->fields_form['input'][] = [ 'type' => 'html', 'label' => '', 'name' => 'thumbnail_field', 'html_content' => $thumbnailHtml, ]; $this->fields_form['input'][] = [ 'type' => 'html', 'label' => '', 'name' => 'categories_field', 'html_content' => $categoriesHtml, ]; return parent::renderForm(); } /* ------------------------------------------------------------------ */ /* Zapis */ /* ------------------------------------------------------------------ */ public function processSave() { $idPost = (int) Tools::getValue('id_post'); $imgDir = _PS_MODULE_DIR_ . self::IMG_DIR; // --- obsługa miniaturki --- if (!empty($_FILES['thumbnail']['name']) && $_FILES['thumbnail']['error'] === UPLOAD_ERR_OK) { if (!is_dir($imgDir)) { @mkdir($imgDir, 0755, true); } $ext = strtolower(pathinfo($_FILES['thumbnail']['name'], PATHINFO_EXTENSION)); $allowed = ['jpg', 'jpeg', 'png', 'webp', 'gif']; if (!in_array($ext, $allowed)) { $this->errors[] = $this->l('Niedozwolony format pliku. Dozwolone: jpg, png, webp, gif.'); return false; } if (@getimagesize($_FILES['thumbnail']['tmp_name']) === false) { $this->errors[] = $this->l('Przesłany plik nie jest prawidłowym obrazem.'); return false; } $filename = md5(uniqid('ppb', true)) . '.' . $ext; if (!move_uploaded_file($_FILES['thumbnail']['tmp_name'], $imgDir . $filename)) { $this->errors[] = $this->l('Błąd podczas przesyłania pliku. Sprawdź uprawnienia katalogu.'); return false; } // usuń starą miniaturkę przy edycji if ($idPost) { $old = new BlogPost($idPost); if (Validate::isLoadedObject($old) && $old->thumbnail) { $oldFile = $imgDir . $old->thumbnail; if (file_exists($oldFile)) { @unlink($oldFile); } } } $_POST['thumbnail'] = $filename; } else { // brak nowego pliku — zachowaj istniejącą miniaturkę if ($idPost) { $existing = new BlogPost($idPost); if (Validate::isLoadedObject($existing) && $existing->thumbnail) { $_POST['thumbnail'] = $existing->thumbnail; } } } // --- auto-generowanie slugów z tytułu --- foreach (Language::getLanguages(false) as $lang) { $lid = (int) $lang['id_lang']; $title = trim((string) Tools::getValue('title_' . $lid)); $slug = trim((string) Tools::getValue('link_rewrite_' . $lid)); if ($slug === '' && $title !== '') { $_POST['link_rewrite_' . $lid] = Tools::link_rewrite($title); } } // --- zapis przez parent (ObjectModel) --- $result = parent::processSave(); // --- zapis kategorii --- if ($result !== false && isset($this->object) && Validate::isLoadedObject($this->object)) { $categoryIds = Tools::getValue('id_category', []); if (!is_array($categoryIds)) { $categoryIds = []; } BlogPost::setCategories((int) $this->object->id, $categoryIds); } return $result; } /* ------------------------------------------------------------------ */ /* Toggle aktywności z listy */ /* ------------------------------------------------------------------ */ public function processStatus() { $post = new BlogPost((int) Tools::getValue($this->identifier)); if (!Validate::isLoadedObject($post)) { $this->errors[] = $this->l('Nie znaleziono wpisu.'); return false; } $post->active = !(bool) $post->active; return (bool) $post->update(); } /* ------------------------------------------------------------------ */ /* Usunięcie — thumbnail i kategorie obsługuje BlogPost::delete() */ /* ------------------------------------------------------------------ */ public function processDelete() { $post = new BlogPost((int) Tools::getValue($this->identifier)); if (!Validate::isLoadedObject($post)) { $this->errors[] = $this->l('Nie znaleziono wpisu.'); return false; } $result = $post->delete(); if (!$result) { $this->errors[] = $this->l('Wystąpił błąd podczas usuwania wpisu.'); } return $result; } }