From 8154fdf768fa67e5c1e028f9a36967d798de9898 Mon Sep 17 00:00:00 2001 From: Jan <96944229+modelrailroader@users.noreply.github.com> Date: Fri, 26 Apr 2024 07:55:19 +0200 Subject: [PATCH] refactor: moved news to twig (#2791) --- phpmyfaq/admin/assets/src/api/index.js | 1 + phpmyfaq/admin/assets/src/api/news.js | 132 ++++ phpmyfaq/admin/assets/src/content/index.js | 1 + phpmyfaq/admin/assets/src/content/news.js | 111 +++ phpmyfaq/admin/assets/src/index.js | 7 +- phpmyfaq/admin/news.php | 647 +++--------------- .../assets/templates/admin/content/news.twig | 415 +++++++++++ phpmyfaq/src/admin-routes.php | 21 + .../Administration/NewsController.php | 174 +++++ phpmyfaq/src/phpMyFAQ/News.php | 18 + phpmyfaq/translations/language_de.php | 3 + phpmyfaq/translations/language_en.php | 3 + 12 files changed, 983 insertions(+), 550 deletions(-) create mode 100644 phpmyfaq/admin/assets/src/api/news.js create mode 100644 phpmyfaq/admin/assets/src/content/news.js create mode 100644 phpmyfaq/assets/templates/admin/content/news.twig create mode 100644 phpmyfaq/src/phpMyFAQ/Controller/Administration/NewsController.php diff --git a/phpmyfaq/admin/assets/src/api/index.js b/phpmyfaq/admin/assets/src/api/index.js index b15fd4e8c9..b04a1d477e 100644 --- a/phpmyfaq/admin/assets/src/api/index.js +++ b/phpmyfaq/admin/assets/src/api/index.js @@ -5,3 +5,4 @@ export * from './group'; export * from './statistics'; export * from './tags'; export * from './user'; +export * from './news'; diff --git a/phpmyfaq/admin/assets/src/api/news.js b/phpmyfaq/admin/assets/src/api/news.js new file mode 100644 index 0000000000..7be837e3b7 --- /dev/null +++ b/phpmyfaq/admin/assets/src/api/news.js @@ -0,0 +1,132 @@ +/** + * Fetch data for news + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + * + * @package phpMyFAQ + * @author Thorsten Rinne + * @author Jan Harms + * @copyright 2024 phpMyFAQ Team + * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @link https://www.phpmyfaq.de + * @since 2024-04-21 + */ +import { pushErrorNotification, pushNotification } from '../utils'; + +export const addNews = async (data = {}) => { + try { + const response = await fetch('api/news/add', { + method: 'POST', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json', + }, + redirect: 'follow', + referrerPolicy: 'no-referrer', + body: JSON.stringify(data), + }); + + const result = await response.json(); + if (result.success) { + pushNotification(result.success); + setTimeout(function () { + window.location.href = '?action=news'; + }, 3000); + } else { + pushErrorNotification(result.error); + } + } catch (error) { + console.error('Error posting news data:', error); + throw error; + } +}; + +export const deleteNews = async (csrfToken, id) => { + try { + const response = await fetch('api/news/delete', { + method: 'DELETE', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json', + }, + redirect: 'follow', + referrerPolicy: 'no-referrer', + body: JSON.stringify({ + csrfToken: csrfToken, + id: id + }), + }); + + const result = await response.json(); + if (result.success) { + pushNotification(result.success); + setTimeout(function () { + window.location.reload(); + }, 3000); + } else { + pushErrorNotification(result.error); + } + } catch (error) { + console.error('Error deleting news data:', error); + throw error; + } +}; + +export const updateNews = async (data = {}) => { + try { + const response = await fetch('api/news/update', { + method: 'PUT', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json', + }, + redirect: 'follow', + referrerPolicy: 'no-referrer', + body: JSON.stringify(data), + }); + + const result = await response.json(); + if (result.success) { + pushNotification(result.success); + setTimeout(function () { + window.location.href = '?action=news'; + }, 3000); + } else { + pushErrorNotification(result.error); + } + } catch (error) { + console.error('Error posting news data:', error); + throw error; + } +}; + +export const activateNews = async (id, status, csrfToken) => { + try { + const response = await fetch('api/news/activate', { + method: 'POST', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json', + }, + redirect: 'follow', + referrerPolicy: 'no-referrer', + body: JSON.stringify({ + id: id, + status: status, + csrfToken: csrfToken + }), + }); + + const result = await response.json(); + if (result.success) { + pushNotification(result.success); + } else { + pushErrorNotification(result.error); + } + } catch (error) { + console.error('Error posting news data:', error); + throw error; + } +}; diff --git a/phpmyfaq/admin/assets/src/content/index.js b/phpmyfaq/admin/assets/src/content/index.js index c3211447a8..a03a2c7190 100644 --- a/phpmyfaq/admin/assets/src/content/index.js +++ b/phpmyfaq/admin/assets/src/content/index.js @@ -9,6 +9,7 @@ export * from './faqs.autocomplete'; export * from './glossary'; export * from './markdown'; export * from './media.browser'; +export * from './news'; export * from './faqs.overview'; export * from './question'; export * from './tags'; diff --git a/phpmyfaq/admin/assets/src/content/news.js b/phpmyfaq/admin/assets/src/content/news.js new file mode 100644 index 0000000000..a7cb4202d3 --- /dev/null +++ b/phpmyfaq/admin/assets/src/content/news.js @@ -0,0 +1,111 @@ +/** + * News administration stuff + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + * + * @package phpMyFAQ + * @author Thorsten Rinne + * @author Jan Harms + * @copyright 2024 phpMyFAQ Team + * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @link https://www.phpmyfaq.de + * @since 2024-04-20 + */ + +import { activateNews, addNews, deleteNews, updateNews } from '../api'; +import { Modal } from 'bootstrap'; +import { TinyMCE } from 'tinymce'; +import tinymce from 'tinymce/tinymce'; + +export const handleAddNews = () => { + const submit = document.getElementById('submitAddNews'); + if (submit) { + submit.addEventListener('click', async (event) => { + event.preventDefault(); + let target = ''; + document.querySelectorAll('#target').forEach(item) => { + if (item.checked) { + target = item.value; + } + }); + + const data = { + dateStart: document.getElementById('dateStart').value, + dateEnd: document.getElementById('dateEnd').value, + news: tinymce.get('editor').getContent(), + newsHeader: document.getElementById('newsheader').value, + authorName: document.getElementById('authorName').value, + authorEmail: document.getElementById('authorEmail').value, + active: document.getElementById('active').checked, + comment: document.getElementById('comment').checked, + link: document.getElementById('link').value, + linkTitle: document.getElementById('linkTitle').value, + langTo: document.getElementById('langTo').value, + target: target, + csrfToken: document.getElementById('pmf-csrf-token').value + } + await addNews(data); + }); + } +} + +export const handleNews = () => { + const deleteNewsButton = document.getElementById('deleteNews'); + if (deleteNewsButton) { + document.querySelectorAll('#deleteNews').forEach(item) => { + item.addEventListener('click', (event) => { + event.preventDefault(); + const modal = new Modal(document.getElementById('confirmDeleteNewsModal')); + document.getElementById('newsId').value = item.getAttribute('data-pmf-newsid'); + modal.show(); + }); + }); + document.getElementById('pmf-delete-news-action').addEventListener('click', async (event) => { + event.preventDefault(); + const csrfToken = document.getElementById('pmf-csrf-token-delete').value; + const id = document.getElementById('newsId').value; + await deleteNews(csrfToken, id); + }); + document.querySelectorAll('#activate').forEach(item) => { + item.addEventListener('click', async () => { + await activateNews(item.getAttribute('data-pmf-id'), item.checked, item.getAttribute('data-pmf-csrf-token')); + }); + }); + } +} + +export const handleEditNews = () => { + const submit = document.getElementById('submitEditNews'); + if (submit) { + submit.addEventListener('click', async (event) => { + event.preventDefault(); + let target = ''; + document.querySelectorAll('#target').forEach(item) => { + if (item.checked) { + target = item.value; + } + }); + + const data = { + id: document.getElementById('id').value, + csrfToken: document.getElementById('pmf-csrf-token').value, + dateStart: document.getElementById('dateStart').value, + dateEnd: document.getElementById('dateEnd').value, + news: tinymce.get('editor').getContent(), + newsHeader: document.getElementById('newsheader').value, + authorName: document.getElementById('authorName').value, + authorEmail: document.getElementById('authorEmail').value, + active: document.getElementById('active').checked, + comment: document.getElementById('comment').checked, + link: document.getElementById('link').value, + linkTitle: document.getElementById('linkTitle').value, + langTo: document.getElementById('langTo').value, + target: target + } + + await updateNews(data); + }); + } +} diff --git a/phpmyfaq/admin/assets/src/index.js b/phpmyfaq/admin/assets/src/index.js index 7eb86eee18..48ad9c555a 100644 --- a/phpmyfaq/admin/assets/src/index.js +++ b/phpmyfaq/admin/assets/src/index.js @@ -43,7 +43,7 @@ import { handleDeleteGlossary, handleAddGlossary, onOpenUpdateGlossaryModal, - handleUpdateGlossary, + handleUpdateGlossary, handleAddNews, handleNews, handleEditNews, } from './content'; import { handleUserList, handleUsers } from './user'; import { handleGroups } from './group'; @@ -133,4 +133,9 @@ document.addEventListener('DOMContentLoaded', async () => { await handleFormEdit(); await handleFormTranslations(); + + // News + await handleAddNews(); + await handleNews(); + await handleEditNews(); }); diff --git a/phpmyfaq/admin/news.php b/phpmyfaq/admin/news.php index 3170e711f4..34092ef3c1 100644 --- a/phpmyfaq/admin/news.php +++ b/phpmyfaq/admin/news.php @@ -17,17 +17,18 @@ */ use phpMyFAQ\Comments; -use phpMyFAQ\Component\Alert; use phpMyFAQ\Date; use phpMyFAQ\Entity\CommentType; use phpMyFAQ\Entity\NewsMessage; use phpMyFAQ\Filter; use phpMyFAQ\Helper\LanguageHelper; -use phpMyFAQ\Language; use phpMyFAQ\News; use phpMyFAQ\Session\Token; -use phpMyFAQ\Strings; +use phpMyFAQ\Template\TwigWrapper; use phpMyFAQ\Translation; +use Twig\Extension\DebugExtension; +use Twig\TwigFilter; +use phpMyFAQ\Configuration; if (!defined('IS_VALID_PHPMYFAQ')) { http_response_code(400); @@ -38,565 +39,113 @@ $csrfToken = Filter::filterInput(INPUT_POST, 'csrf', FILTER_SANITIZE_SPECIAL_CHARS); -if ('add-news' == $action && $user->perm->hasPermission($user->getUserId(), 'addnews')) { ?> -
-

- - -

-
- -
-
-
- getTokenInput('save-news') ?> - -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
-
- - -
-
- - -
-
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
-
- - -
-
- - -
-
- - -
-
-
- -
- -
- -
-
- -
-
- -
- -
-
- -
- -
- -
-
- -
-
- - - - -
-
- -
-
-
- - - $action, + 'permissionAddNews' => $user->perm->hasPermission($user->getUserId(), 'addnews'), + 'permissionEditNews' => $user->perm->hasPermission($user->getUserId(), 'editnews'), + 'permissionDeleteNews' => $user->perm->hasPermission($user->getUserId(), 'delnews'), + 'defaultUrl' => $faqConfig->getDefaultUrl(), + 'enableWysiwyg' => $faqConfig->get('main.enableWysiwygEditor'), + 'ad_news_add' => Translation::get('ad_news_add'), + 'csrfToken_saveNews' => Token::getInstance()->getTokenString('save-news'), + 'ad_news_author_name' => Translation::get('ad_news_author_name'), + 'ad_news_set_active' => Translation::get('ad_news_set_active'), + 'ad_news_link_url' => Translation::get('ad_news_link_url'), + 'ad_news_link_title' => Translation::get('ad_news_link_title'), + 'ad_news_link_target' => Translation::get('ad_news_link_target'), + 'ad_news_link_window' => Translation::get('ad_news_link_window'), + 'ad_news_link_faq' => Translation::get('ad_news_link_faq'), + 'ad_news_link_parent' => Translation::get('ad_news_link_parent'), + 'selectLanguage' => LanguageHelper::renderSelectLanguage($faqLangCode, false, [], 'langTo'), + 'ad_news_expiration_window' => Translation::get('ad_news_expiration_window'), + 'ad_news_from' => Translation::get('ad_news_from'), + 'ad_news_to' => Translation::get('ad_news_to'), + 'ad_entry_back' => Translation::get('ad_entry_back'), + 'ad_menu_news_add' => Translation::get('ad_menu_news_add'), + 'ad_news_headline' => Translation::get('ad_news_headline'), + 'ad_news_date' => Translation::get('ad_news_date'), + 'ad_news_update' => Translation::get('ad_news_update'), + 'ad_news_delete' => Translation::get('ad_news_delete'), + 'ad_news_nodata' => Translation::get('ad_news_nodata'), + 'ad_news_edit' => Translation::get('ad_news_edit'), + 'ad_news_header' => Translation::get('ad_news_header'), + 'ad_news_text' => Translation::get('ad_news_text'), + 'ad_news_allowComments' => Translation::get('ad_news_allowComments'), + 'ad_entry_locale' => Translation::get('ad_entry_locale'), + 'ad_entry_comment' => Translation::get('ad_entry_comment'), + 'ad_entry_commentby' => Translation::get('ad_entry_commentby'), + 'newsCommentDate' => Translation::get('newsCommentDate'), + 'ad_news_insertfail' => Translation::get('ad_news_insertfail'), + 'msgNews' => Translation::get('msgNews'), + 'ad_news_data' => Translation::get('ad_news_data'), + 'ad_news_del' => Translation::get('ad_news_del'), + 'ad_news_nodelete' => Translation::get('ad_news_nodelete'), + 'ad_news_yesdelete' => Translation::get('ad_news_yesdelete'), + 'ad_news_delsuc' => Translation::get('ad_news_delsuc'), + 'ad_news_updatesuc' => Translation::get('ad_news_updatesuc'), + 'msgDeleteNews' => Translation::get('msgDeleteNews'), + 'csrfToken_deleteNews' => Token::getInstance()->getTokenString('delete-news'), + 'csrfToken_updateNews' => Token::getInstance()->getTokenString('update-news'), + 'ad_entry_active' => Translation::get('ad_entry_active'), + 'csrfToken_activateNews' => Token::getInstance()->getTokenString('activate-news') +]; + +$filterCreateIsoDate = new TwigFilter('createIsoDate', function ($string) { + return Date::createIsoDate($string); +}); + +$filterFormatDate = new TwigFilter('formatDate', function ($string) { + $faqConfig = Configuration::getConfigurationInstance(); + $date = new Date($faqConfig); + return $date->format($string); +}); + +if ('add-news' == $action && $user->perm->hasPermission($user->getUserId(), 'addnews')) { + $templateVars = [ + ...$templateVars, + 'userEmail' => $user->getUserData('email'), + 'userName' => $user->getUserData('display_name') + ]; } elseif ('news' == $action && $user->perm->hasPermission($user->getUserId(), 'editnews')) { - ?> -
-

- -

-
- -
-
+ $newsHeaders = $news->getHeader(); -
-
- - - - - - - - - - getHeader(); - $date = new Date($faqConfig); - if (is_countable($newsHeader) ? count($newsHeader) : 0) { - foreach ($newsHeader as $newsItem) { - ?> - - - - - - - ', - Translation::get('ad_news_nodata') - ); - } - ?> - -
 
format($newsItem['date']) ?> - - - - - - - -
%s
-
-
- $newsHeaders, + ]; } elseif ('edit-news' == $action && $user->perm->hasPermission($user->getUserId(), 'editnews')) { $id = Filter::filterInput(INPUT_GET, 'id', FILTER_VALIDATE_INT); $newsData = $news->get($id, true); - ?> -
-

- - -

-
- -
-
-
- - getTokenInput('update-news') ?> -
- -
- -
-
- -
- -
- - -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
-
- > - -
-
- > - -
-
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
-
- > - -
-
- > - -
-
- > - -
-
-
- -
- -
- -
-
- -
- -
- -
-
-
- -
- -
-
- -
-
- - - - -
-
-
- getCommentsData($newsId, CommentType::NEWS); - if ((is_countable($comments) ? count($comments) : 0) > 0) { - ?> -
- -
- - - - :
-
- - - - -
-
-
- perm->hasPermission($user->getUserId(), 'addnews')) { - ?> -
-

- - -

-
- -
-
- setLanguage($newsLang) - ->setHeader($header) - ->setMessage(html_entity_decode((string) $content)) - ->setAuthor($author) - ->setEmail($email) - ->setActive(!is_null($active)) - ->setComment(!is_null($comment)) - ->setDateStart(new DateTime($dateStart)) - ->setDateEnd(new DateTime($dateEnd)) - ->setLink($link ?? '') - ->setLinkTitle($linkTitle ?? '') - ->setLinkTarget($target ?? '') - ->setCreated(new DateTime()); - - if ($news->create($newsMessage)) { - echo Alert::success('ad_news_updatesuc'); - } else { - echo Alert::danger('ad_news_insertfail', $faqConfig->getDb()->error()); - } - printf('
%s

', Translation::get('msgNews')); - ?> -
-
- perm->hasPermission($user->getUserId(), 'editnews')) { - ?> -
-

- - -

-
- -
-
- setId($newsId) - ->setLanguage($newsLang) - ->setHeader($header) - ->setMessage(html_entity_decode((string) $content)) - ->setAuthor($author) - ->setEmail($email) - ->setActive(!is_null($active)) - ->setComment(!is_null($comment)) - ->setDateStart(new DateTime($dateStart)) - ->setDateEnd(new DateTime($dateEnd)) - ->setLink($link ?? '') - ->setLinkTitle($linkTitle ?? '') - ->setLinkTarget($target ?? '') - ->setCreated(new DateTime()); - - if ($news->update($newsMessage)) { - echo Alert::success('ad_news_updatesuc'); - } else { - echo Alert::danger('ad_news_updatefail', $faqConfig->getDb()->error()); - } - printf('
%s

', Translation::get('msgNews')); - ?> -
-
- perm->hasPermission($user->getUserId(), 'delnews')) { - ?> -
-

- - -

-
- -
-
- -
-
-
- - - getTokenInput('delete-news') ?> - - - - -
-
- - verifyToken('delete-news', $csrfToken)) { - $deleteId = Filter::filterInput(INPUT_POST, 'id', FILTER_VALIDATE_INT); - $news->delete((int)$deleteId); - echo Alert::success('ad_news_delsuc'); - printf('
%s

', Translation::get('msgNews')); - } - } + $templateVars = [ + ...$templateVars, + 'newsData' => $newsData, + 'newsDataContent' => (isset($newsData['content']) ? htmlspecialchars( + (string)$newsData['content'], + ENT_QUOTES + ) : ''), + 'dateStart' => $dateStart, + 'dateEnd' => $dateEnd, + 'comments' => $comments, + 'newsId' => $newsId, + 'commentTypeNews' => CommentType::NEWS + ]; } else { require __DIR__ . '/no-permission.php'; + exit(); } + +$twig = new TwigWrapper(PMF_ROOT_DIR . '/assets/templates'); +$twig->addExtension(new DebugExtension()); +$twig->addFilter($filterFormatDate); +$twig->addFilter($filterCreateIsoDate); +$template = $twig->loadTemplate('./admin/content/news.twig'); + +echo $template->render($templateVars); diff --git a/phpmyfaq/assets/templates/admin/content/news.twig b/phpmyfaq/assets/templates/admin/content/news.twig new file mode 100644 index 0000000000..bd89ba2eb2 --- /dev/null +++ b/phpmyfaq/assets/templates/admin/content/news.twig @@ -0,0 +1,415 @@ +{% if action == 'add-news' and permissionAddNews == true %} +
+

+ + {{ ad_news_add }} +

+
+ +
+
+
+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ + +
+
+ + +
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+ {{ selectLanguage|raw }} +
+
+ +
{{ ad_news_expiration_window }}
+
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+
+
+{% endif %} +{% if action == 'news' and permissionEditNews == true %} +
+

+ {{ msgNews }} +

+ {% if permissionAddNews == true %} + + {% endif %} +
+ +
+
+ + + + + + + + + + + {% if news|length > 0 %} + {% for newsItem in news %} + + + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
{{ ad_news_headline }}{{ ad_news_date }} {{ ad_entry_active }}
{{ newsItem.header|escape }}{{ newsItem.date|formatDate }} + {% if permissionEditNews == true %} + + + + {% endif %} + + {% if permissionDeleteNews == true %} + + + + {% endif %} + + {% if permissionEditNews == true %} + + {% endif %} +
{{ ad_news_nodata }}
+
+
+ +{% endif %} +{% if action == 'edit-news' and permissionEditNews == true %} +
+

+ + {{ ad_news_edit }} +

+
+ +
+
+
+ + + +
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ + +
+
+ + +
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+ {{ selectLanguage|raw }} +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+ {% if comments|length > 0 %} +
{{ ad_entry_comment }}
+ {% endif %} + {% for item in comments %} +
+ {{ ad_entry_commentby }} + + {{ item.user }} + :
+ {{ item.content }}
+ {{ newsCommentDate }}{{ item.date|createIsoDate }} + + + +
+
+
+ {% endfor %} +{% endif %} diff --git a/phpmyfaq/src/admin-routes.php b/phpmyfaq/src/admin-routes.php index 3d3c8ebf61..8f4aeaf25e 100644 --- a/phpmyfaq/src/admin-routes.php +++ b/phpmyfaq/src/admin-routes.php @@ -40,6 +40,7 @@ use phpMyFAQ\Controller\Administration\UserController; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use phpMyFAQ\Controller\Administration\NewsController; $routes = new RouteCollection(); @@ -614,4 +615,24 @@ ) ); +// +// News API +// +$routes->add( + 'admin.api.news.add', + new Route('/news/add', ['_controller' => [NewsController::class, 'addNews'], '_methods' => 'POST']) +); +$routes->add( + 'admin.api.news.delete', + new Route('/news/delete', ['_controller' => [NewsController::class, 'deleteNews'], '_methods' => 'DELETE']) +); +$routes->add( + 'admin.api.news.update', + new Route('/news/update', ['_controller' => [NewsController::class, 'updateNews'], '_methods' => 'PUT']) +); +$routes->add( + 'admin.api.news.activate', + new Route('/news/activate', ['_controller' => [NewsController::class, 'activateNews'], '_methods' => 'POST']) +); + return $routes; diff --git a/phpmyfaq/src/phpMyFAQ/Controller/Administration/NewsController.php b/phpmyfaq/src/phpMyFAQ/Controller/Administration/NewsController.php new file mode 100644 index 0000000000..45e188e4b3 --- /dev/null +++ b/phpmyfaq/src/phpMyFAQ/Controller/Administration/NewsController.php @@ -0,0 +1,174 @@ + + * @author Jan Harms + * @copyright 2024 phpMyFAQ Team + * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @link https://www.phpmyfaq.de + * @since 2024-04-20 + */ + +namespace phpMyFAQ\Controller\Administration; + +use DateTime; +use phpMyFAQ\Configuration; +use phpMyFAQ\Controller\AbstractController; +use phpMyFAQ\Entity\NewsMessage; +use phpMyFAQ\Enums\PermissionType; +use phpMyFAQ\Filter; +use phpMyFAQ\News; +use phpMyFAQ\Session\Token; +use phpMyFAQ\Translation; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class NewsController extends AbstractController +{ + #[Route('admin/api/news/add')] + public function addNews(Request $request) + { + $this->userHasPermission(PermissionType::NEWS_ADD); + $data = json_decode($request->getContent()); + + $news = new News($this->configuration); + + if (!Token::getInstance()->verifyToken('save-news', $data->csrfToken)) { + return $this->json(['error' => Translation::get('err_NotAuth')], Response::HTTP_UNAUTHORIZED); + } + + $dateStart = Filter::filterVar($data->dateStart, FILTER_SANITIZE_SPECIAL_CHARS); + $dateEnd = Filter::filterVar($data->dateEnd, FILTER_SANITIZE_SPECIAL_CHARS); + $header = Filter::filterVar($data->newsHeader, FILTER_SANITIZE_SPECIAL_CHARS); + $content = Filter::filterVar($data->news, FILTER_SANITIZE_SPECIAL_CHARS); + $author = Filter::filterVar($data->authorName, FILTER_SANITIZE_SPECIAL_CHARS); + $email = Filter::filterVar($data->authorEmail, FILTER_VALIDATE_EMAIL); + $active = Filter::filterVar($data->active, FILTER_SANITIZE_SPECIAL_CHARS); + $comment = Filter::filterVar($data->comment, FILTER_SANITIZE_SPECIAL_CHARS); + $link = Filter::filterVar($data->link, FILTER_SANITIZE_SPECIAL_CHARS); + $linkTitle = Filter::filterVar($data->linkTitle, FILTER_SANITIZE_SPECIAL_CHARS); + $newsLang = Filter::filterVar($data->langTo, FILTER_SANITIZE_SPECIAL_CHARS); + $target = Filter::filterVar($data->target, FILTER_SANITIZE_SPECIAL_CHARS); + + $newsMessage = new NewsMessage(); + $newsMessage + ->setLanguage($newsLang) + ->setHeader($header) + ->setMessage(html_entity_decode((string)$content)) + ->setAuthor($author) + ->setEmail($email) + ->setActive($active) + ->setComment($comment) + ->setDateStart(new DateTime($dateStart)) + ->setDateEnd(new DateTime($dateEnd)) + ->setLink($link ?? '') + ->setLinkTitle($linkTitle ?? '') + ->setLinkTarget($target ?? '') + ->setCreated(new DateTime()); + + if ($news->create($newsMessage)) { + return $this->json(['success' => Translation::get('ad_news_updatesuc')], Response::HTTP_OK); + } else { + return $this->json(['error' => Translation::get('ad_news_insertfail')], Response::HTTP_BAD_GATEWAY); + } + } + + #[Route('admin/api/news/delete')] + public function deleteNews(Request $request) + { + $this->userHasPermission(PermissionType::NEWS_DELETE); + $data = json_decode($request->getContent()); + + $news = new News($this->configuration); + + if (!Token::getInstance()->verifyToken('delete-news', $data->csrfToken)) { + return $this->json(['error' => Translation::get('err_NotAuth')], Response::HTTP_UNAUTHORIZED); + } + + $deleteId = Filter::filterVar($data->id, FILTER_VALIDATE_INT); + + if ($news->delete((int)$deleteId)) { + return $this->json(['success' => Translation::get('ad_news_delsuc')], Response::HTTP_OK); + } else { + return $this->json(['error' => Translation::get('ad_news_updatefail')], Response::HTTP_BAD_GATEWAY); + } + } + + #[Route('admin/api/news/update')] + public function updateNews(Request $request) + { + $this->userHasPermission(PermissionType::NEWS_DELETE); + $data = json_decode($request->getContent()); + + $news = new News($this->configuration); + + if (!Token::getInstance()->verifyToken('update-news', $data->csrfToken)) { + return $this->json(['error' => Translation::get('err_NotAuth')], Response::HTTP_UNAUTHORIZED); + } + + $newsId = Filter::filterVar($data->id, FILTER_VALIDATE_INT); + $dateStart = Filter::filterVar($data->dateStart, FILTER_SANITIZE_SPECIAL_CHARS); + $dateEnd = Filter::filterVar($data->dateEnd, FILTER_SANITIZE_SPECIAL_CHARS); + $header = Filter::filterVar($data->newsHeader, FILTER_SANITIZE_SPECIAL_CHARS); + $content = Filter::filterVar($data->news, FILTER_SANITIZE_SPECIAL_CHARS); + $author = Filter::filterVar($data->authorName, FILTER_SANITIZE_SPECIAL_CHARS); + $email = Filter::filterVar($data->authorEmail, FILTER_VALIDATE_EMAIL); + $active = Filter::filterVar($data->active, FILTER_SANITIZE_SPECIAL_CHARS); + $comment = Filter::filterVar($data->comment, FILTER_SANITIZE_SPECIAL_CHARS); + $link = Filter::filterVar($data->link, FILTER_SANITIZE_SPECIAL_CHARS); + $linkTitle = Filter::filterVar($data->linkTitle, FILTER_SANITIZE_SPECIAL_CHARS); + $newsLang = Filter::filterVar($data->langTo, FILTER_SANITIZE_SPECIAL_CHARS); + $target = Filter::filterVar($data->target, FILTER_SANITIZE_SPECIAL_CHARS); + + $newsMessage = new NewsMessage(); + $newsMessage + ->setId($newsId) + ->setLanguage($newsLang) + ->setHeader($header) + ->setMessage(html_entity_decode((string)$content)) + ->setAuthor($author) + ->setEmail($email) + ->setActive($active) + ->setComment($comment) + ->setDateStart(new DateTime($dateStart)) + ->setDateEnd(new DateTime($dateEnd)) + ->setLink($link ?? '') + ->setLinkTitle($linkTitle ?? '') + ->setLinkTarget($target ?? '') + ->setCreated(new DateTime()); + + if ($news->update($newsMessage)) { + return $this->json(['success' => Translation::get('ad_news_updatesuc')], Response::HTTP_OK); + } else { + return $this->json(['error' => Translation::get('ad_news_updatefail')], Response::HTTP_BAD_GATEWAY); + } + } + + #[Route('admin/api/news/activate')] + public function activateNews(Request $request) + { + $this->userHasPermission(PermissionType::NEWS_EDIT); + $data = json_decode($request->getContent()); + + $news = new News($this->configuration); + + if (!Token::getInstance()->verifyToken('activate-news', $data->csrfToken)) { + return $this->json(['error' => Translation::get('err_NotAuth')], Response::HTTP_UNAUTHORIZED); + } + + $id = Filter::filterVar($data->id, FILTER_VALIDATE_INT); + $status = Filter::filterVar($data->status, FILTER_SANITIZE_SPECIAL_CHARS); + + if ($news->activate($id, $status)) { + return $this->json(['success' => Translation::get('ad_news_updatesuc')], Response::HTTP_OK); + } else { + return $this->json(['error' => Translation::get('ad_news_updatefail')], Response::HTTP_BAD_GATEWAY); + } + } +} diff --git a/phpmyfaq/src/phpMyFAQ/News.php b/phpmyfaq/src/phpMyFAQ/News.php index d993efa30e..6eb1216f88 100644 --- a/phpmyfaq/src/phpMyFAQ/News.php +++ b/phpmyfaq/src/phpMyFAQ/News.php @@ -363,4 +363,22 @@ public function delete(int $id): bool return (bool) $this->configuration->getDb()->query($query); } + + /** + * Activates/Deactivates a news message + * + * @param int $id News ID + * @param bool $status Status of activation + */ + public function activate(int $id, bool $status): bool + { + $query = sprintf( + "UPDATE %sfaqnews SET active = '%s' WHERE id = %d", + Database::getTablePrefix(), + $status ? 'y' : 'n', + $id + ); + + return (bool) $this->configuration->getDb()->query($query); + } } diff --git a/phpmyfaq/translations/language_de.php b/phpmyfaq/translations/language_de.php index 3b322f3202..6d0fb940fc 100755 --- a/phpmyfaq/translations/language_de.php +++ b/phpmyfaq/translations/language_de.php @@ -1417,4 +1417,7 @@ $PMF_LANG['msgOnlyInactiveFAQs'] = 'Nur inaktive FAQs'; $PMF_LANG['msgOnlyNewFAQs'] = 'Nur neue FAQs'; +// added v4.0.0-alpha.2 - 2024-04-21 by Jan +$PMF_LANG['msgDeleteNews'] = 'News löschen'; + return $PMF_LANG; diff --git a/phpmyfaq/translations/language_en.php b/phpmyfaq/translations/language_en.php index ee672bc24f..84eff2fb5e 100644 --- a/phpmyfaq/translations/language_en.php +++ b/phpmyfaq/translations/language_en.php @@ -1437,4 +1437,7 @@ $PMF_LANG['msgOnlyInactiveFAQs'] = 'Only inactive FAQs'; $PMF_LANG['msgOnlyNewFAQs'] = 'Only new FAQs'; +// added v4.0.0-alpha.2 - 2024-04-21 by Jan +$PMF_LANG['msgDeleteNews'] = 'Delete news'; + return $PMF_LANG;