- Created SQL installation scripts for categories and posts tables. - Added uninstall scripts to drop the created tables. - Introduced CSS styles for blog layout, including responsive design for posts and categories. - Implemented PHP redirection in index files to prevent direct access. - Developed Smarty templates for blog category tree, post list, and individual post details. - Ensured proper caching headers in PHP files to enhance performance.
378 lines
14 KiB
PHP
378 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Project-Pro Blog — Admin Posts Controller
|
|
*
|
|
* @author Project-Pro <https://www.project-pro.pl>
|
|
* @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 = '<div class="form-group">';
|
|
$thumbnailHtml .= '<label class="control-label col-lg-3">' . $this->l('Miniaturka') . '</label>';
|
|
$thumbnailHtml .= '<div class="col-lg-9">';
|
|
|
|
if ($existingThumbnail) {
|
|
$url = Context::getContext()->link->getBaseLink()
|
|
. 'modules/projectproblog/views/img/posts/'
|
|
. $existingThumbnail;
|
|
$thumbnailHtml .= '<div style="margin-bottom:10px;">'
|
|
. '<img src="' . $url . '" style="max-height:150px;max-width:300px;border:1px solid #ddd;padding:4px;" />'
|
|
. '</div>';
|
|
}
|
|
|
|
$thumbnailHtml .= '<input type="file" name="thumbnail" id="thumbnail_upload" '
|
|
. 'accept="image/jpeg,image/png,image/webp,image/gif" class="form-control-file">';
|
|
$thumbnailHtml .= '<p class="help-block">' . $this->l('Dozwolone formaty: jpg, png, webp, gif.') . '</p>';
|
|
$thumbnailHtml .= '</div></div>';
|
|
|
|
// --- 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 = '<div class="form-group">';
|
|
$categoriesHtml .= '<label class="control-label col-lg-3">'
|
|
. $this->l('Kategorie')
|
|
. ' <span class="help-box" data-toggle="tooltip" title="'
|
|
. $this->l('Wpis może być przypisany do wielu kategorii.') . '"></span>'
|
|
. '</label>';
|
|
$categoriesHtml .= '<div class="col-lg-9">';
|
|
|
|
if (!empty($allCategories)) {
|
|
$categoriesHtml .= '<div style="max-height:200px;overflow-y:auto;border:1px solid #ddd;padding:10px;background:#fff;">';
|
|
foreach ($allCategories as $cat) {
|
|
$checked = in_array((int) $cat['id_category'], $selectedCategories) ? ' checked' : '';
|
|
$categoriesHtml .= '<div class="checkbox" style="margin:4px 0;">'
|
|
. '<label>'
|
|
. '<input type="checkbox" name="id_category[]" value="' . (int) $cat['id_category'] . '"' . $checked . '> '
|
|
. htmlspecialchars($cat['name'], ENT_QUOTES, 'UTF-8')
|
|
. '</label>'
|
|
. '</div>';
|
|
}
|
|
$categoriesHtml .= '</div>';
|
|
} else {
|
|
$categoriesHtml .= '<p class="text-muted">'
|
|
. $this->l('Brak aktywnych kategorii. Dodaj kategorie w zakładce "Kategorie bloga".')
|
|
. '</p>';
|
|
}
|
|
|
|
$categoriesHtml .= '</div></div>';
|
|
|
|
// --- 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;
|
|
}
|
|
}
|