mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-10-12 15:04:04 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7275d6e897 | ||
|
|
349a0641cd | ||
|
|
304870f18c | ||
|
|
71e3cdb78d | ||
|
|
a0ea54d0c0 | ||
|
|
de627171d5 | ||
|
|
6f3c0fb174 | ||
|
|
76130bbf2c | ||
|
|
708ffac47e | ||
|
|
ab854067eb | ||
|
|
b59c962f60 | ||
|
|
d57985b90a |
@@ -61,6 +61,15 @@ $route['api/v1/organisation/[O|o]rganisationseinheit/(:any)'] = 'api/v1/organisa
|
|||||||
$route['api/v1/ressource/[B|b]etriebsmittelperson/(:any)'] = 'api/v1/ressource/betriebsmittelperson2/$1';
|
$route['api/v1/ressource/[B|b]etriebsmittelperson/(:any)'] = 'api/v1/ressource/betriebsmittelperson2/$1';
|
||||||
$route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
|
$route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
|
||||||
|
|
||||||
|
// Legacy CMS news routes
|
||||||
|
$route['api/frontend/v1/Cms/getNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/getNewsItem/$1';
|
||||||
|
$route['api/frontend/v1/Cms/storeNewsItem'] = 'api/frontend/v1/NewsAdministrationAPI/storeNewsItem';
|
||||||
|
$route['api/frontend/v1/Cms/updateNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/updateNewsItem/$1';
|
||||||
|
$route['api/frontend/v1/Cms/deleteNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/deleteNewsItem/$1';
|
||||||
|
|
||||||
|
// Keep legacy uppercase URLs refreshable while using the canonical controller method casing
|
||||||
|
$route['CisVue/Cms/NewsAdministration'] = 'CisVue/Cms/newsAdministration';
|
||||||
|
|
||||||
$route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1';
|
$route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1';
|
||||||
$route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1';
|
$route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1';
|
||||||
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
|
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
|
||||||
|
|||||||
@@ -17,14 +17,15 @@ class Cms extends Auth_Controller
|
|||||||
array(
|
array(
|
||||||
'content' => 'basis/cis:r',
|
'content' => 'basis/cis:r',
|
||||||
'getNews' => 'basis/cis:r',
|
'getNews' => 'basis/cis:r',
|
||||||
'getNewsRowCount' => 'basis/cis:r',
|
|
||||||
'getRoomInformation' => 'basis/cis:r',
|
'getRoomInformation' => 'basis/cis:r',
|
||||||
'news' => 'basis/cis:r'
|
'news' => 'basis/cis:r',
|
||||||
|
'newsAdministration' => ['basis/news:r'],
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Loads Libraries
|
// Loads Libraries
|
||||||
$this->load->library('CmsLib');
|
$this->load->library('CmsLib');
|
||||||
|
$this->load->library('PermissionLib');
|
||||||
|
|
||||||
// Loads phrases system
|
// Loads phrases system
|
||||||
$this->loadPhrases([
|
$this->loadPhrases([
|
||||||
@@ -84,9 +85,29 @@ class Cms extends Auth_Controller
|
|||||||
public function news($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true)
|
public function news($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true)
|
||||||
{
|
{
|
||||||
$viewData = array();
|
$viewData = array();
|
||||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData'=>$viewData, 'route' => 'News']);
|
$this->load->view('CisRouterView/CisRouterView.php', [
|
||||||
|
'viewData'=>$viewData,
|
||||||
|
'route' => 'News',
|
||||||
|
'permissions' => [
|
||||||
|
'basis/news_r' => $this->permissionlib->isBerechtigt('basis/news', 's'),
|
||||||
|
'basis/news_w' => $this->permissionlib->isBerechtigt('basis/news', 'suid'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newsAdministration()
|
||||||
|
{
|
||||||
|
$viewData = array();
|
||||||
|
$this->load->view('CisRouterView/CisRouterView.php', [
|
||||||
|
'viewData'=>$viewData,
|
||||||
|
'route' => 'NewsAdministration',
|
||||||
|
'permissions' => [
|
||||||
|
'basis/news_r' => $this->permissionlib->isBerechtigt('basis/news', 's'),
|
||||||
|
'basis/news_w' => $this->permissionlib->isBerechtigt('basis/news', 'suid'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function getRoomInformation($ort_kurzbz)
|
public function getRoomInformation($ort_kurzbz)
|
||||||
{
|
{
|
||||||
// Load Config
|
// Load Config
|
||||||
|
|||||||
@@ -36,14 +36,15 @@ class Cms extends FHCAPI_Controller
|
|||||||
'ContentID' => self::PERM_LOGGED,
|
'ContentID' => self::PERM_LOGGED,
|
||||||
'getOrtKurzbzContent' => self::PERM_LOGGED,
|
'getOrtKurzbzContent' => self::PERM_LOGGED,
|
||||||
'content' => self::PERM_LOGGED,
|
'content' => self::PERM_LOGGED,
|
||||||
'news' => self::PERM_LOGGED,
|
'newsForWidget' => self::PERM_LOGGED,
|
||||||
'getNewsRowCount' => self::PERM_LOGGED,
|
|
||||||
'getNews' => self::PERM_LOGGED,
|
'getNews' => self::PERM_LOGGED,
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->load->model('content/News_model', 'NewsModel');
|
$this->load->model('content/News_model', 'NewsModel');
|
||||||
|
$this->load->model('crm/Student_model', 'StudentModel');
|
||||||
|
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
|
||||||
|
|
||||||
// setting up the papgination_size
|
// setting up the papgination_size
|
||||||
$this->page_size = 10;
|
$this->page_size = 10;
|
||||||
|
|
||||||
@@ -59,7 +60,6 @@ class Cms extends FHCAPI_Controller
|
|||||||
//------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
// Private methods
|
// Private methods
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
// Public methods
|
// Public methods
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ class Cms extends FHCAPI_Controller
|
|||||||
$this->terminateWithSuccess($content_id);
|
$this->terminateWithSuccess($content_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function news()
|
public function newsForWidget()
|
||||||
{
|
{
|
||||||
|
|
||||||
// form validation
|
// form validation
|
||||||
@@ -118,7 +118,7 @@ class Cms extends FHCAPI_Controller
|
|||||||
$this->load->model('content/news_model', 'NewsModel');
|
$this->load->model('content/news_model', 'NewsModel');
|
||||||
|
|
||||||
$limit = $this->input->get('limit',TRUE);
|
$limit = $this->input->get('limit',TRUE);
|
||||||
|
|
||||||
//query the news
|
//query the news
|
||||||
$news = $this->NewsModel->getAll($limit);
|
$news = $this->NewsModel->getAll($limit);
|
||||||
|
|
||||||
@@ -157,23 +157,7 @@ class Cms extends FHCAPI_Controller
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNewsRowCount($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $fachbereich_kurzbz = null, $maxalter = 0, $edit = false, $sichtbar = true, $page = 1, $page_size = 10)
|
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = false, $titel = '', $edit = false, $sichtbar = true)
|
||||||
{
|
|
||||||
list($studiengang_kz, $semester) = $this->cmslib->getStgAndSem($studiengang_kz, $semester);
|
|
||||||
$all = $edit;
|
|
||||||
|
|
||||||
$this->load->model('content/News_model','NewsModel');
|
|
||||||
|
|
||||||
$num_rows = $this->NewsModel->countNewsWithContent(getSprache(), $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen);
|
|
||||||
|
|
||||||
$num_rows = $this->getDataOrTerminateWithError($num_rows);
|
|
||||||
|
|
||||||
$this->terminateWithSuccess($num_rows);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true)
|
|
||||||
{
|
{
|
||||||
//form validation
|
//form validation
|
||||||
$this->load->library('form_validation');
|
$this->load->library('form_validation');
|
||||||
@@ -193,15 +177,45 @@ class Cms extends FHCAPI_Controller
|
|||||||
|
|
||||||
// default value for the page_size is 10
|
// default value for the page_size is 10
|
||||||
$page_size = $page_size ?? 10;
|
$page_size = $page_size ?? 10;
|
||||||
|
|
||||||
|
$passedSichtbar = $this->input->get('published', true);
|
||||||
|
if($passedSichtbar !== null)
|
||||||
|
{
|
||||||
|
$sichtbar = $passedSichtbar;
|
||||||
|
}
|
||||||
|
|
||||||
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size, $sprache);
|
$filterForDegreePrograms = false;
|
||||||
|
$entitledDegreePrograms = [];
|
||||||
|
$student = $this->StudentModel->loadWhere(['student_uid' => getAuthUID()]);
|
||||||
|
if(isError($student))
|
||||||
|
{
|
||||||
|
$this->terminateWithError(getError($student));
|
||||||
|
}
|
||||||
|
if(hasData($student))
|
||||||
|
{
|
||||||
|
$student = current(getData($student));
|
||||||
|
|
||||||
|
$degreeProgramSemesterDataResult = $this->PrestudentModel->getStudiengaengAndAusbildungssemesterByStudentUid(getAuthUID());
|
||||||
|
$degreeProgramSemesterData = $this->getDataOrTerminateWithError($degreeProgramSemesterDataResult)[0];
|
||||||
|
if ($degreeProgramSemesterData) {
|
||||||
|
$studiengang_kz = isset($degreeProgramSemesterData->studiengang_kz) ? $degreeProgramSemesterData->studiengang_kz : null;
|
||||||
|
$semester = isset($degreeProgramSemesterData->ausbildungssemester) ? $degreeProgramSemesterData->ausbildungssemester : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mischen = true;
|
||||||
|
} else {
|
||||||
|
$filterForDegreePrograms = true;
|
||||||
|
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news");
|
||||||
|
array_push($entitledDegreePrograms, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size, $sprache, $filterForDegreePrograms, $entitledDegreePrograms, true);
|
||||||
$news = $this->getDataOrTerminateWithError($news);
|
$news = $this->getDataOrTerminateWithError($news);
|
||||||
|
|
||||||
$this->addMeta('phrases', json_decode($this->p->getJson()));
|
$this->addMeta('row_count', $news["row_count"] ?? 0);
|
||||||
$this->terminateWithSuccess($news);
|
$this->terminateWithSuccess($news["content"]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,937 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2026 fhcomplete.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('BASEPATH')) exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class NewsAdministrationAPI extends FHCAPI_Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* News API constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct([
|
||||||
|
'getNews' => 'basis/news:r',
|
||||||
|
'getNewsItem' => 'basis/news:r',
|
||||||
|
'storeNewsItem' => 'basis/news:w',
|
||||||
|
'updateNewsItem' => 'basis/news:w',
|
||||||
|
'deleteNewsItem' => 'basis/news:w'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->load->model('content/Content_model', 'ContentModel');
|
||||||
|
$this->load->model('content/Contentsprache_model', 'ContentspracheModel');
|
||||||
|
$this->load->model('content/News_model', 'NewsModel');
|
||||||
|
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
|
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
|
||||||
|
$this->load->model('system/Sprache_model', 'SpracheModel');
|
||||||
|
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
|
||||||
|
|
||||||
|
$this->loadPhrases([
|
||||||
|
'global'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->load->library('CmsLib');
|
||||||
|
|
||||||
|
$this->load->helper('hlp_sancho_helper');
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Public methods
|
||||||
|
|
||||||
|
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $titel = '', $active = true, $sichtbar = true)
|
||||||
|
{
|
||||||
|
$this->load->library('form_validation');
|
||||||
|
$this->form_validation->set_data($_GET);
|
||||||
|
$this->form_validation->set_rules('page','Page','required|is_natural');
|
||||||
|
$this->form_validation->set_rules('page_size', 'PageSize', 'is_natural');
|
||||||
|
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||||
|
|
||||||
|
$page = intval($this->input->get('page', true));
|
||||||
|
$page_size = intval($this->input->get('page_size', true));
|
||||||
|
$sprache = $this->input->get('sprache', true);
|
||||||
|
if(!$sprache)
|
||||||
|
{
|
||||||
|
$sprache = getUserLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// default value for the page_size is 10
|
||||||
|
$page_size = $page_size ?? 10;
|
||||||
|
|
||||||
|
$passedSichtbar = $this->input->get('published', true);
|
||||||
|
if($passedSichtbar !== null)
|
||||||
|
{
|
||||||
|
$sichtbar = $passedSichtbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
$passedIsActive = $this->input->get('isActive', true);
|
||||||
|
if ($passedIsActive !== null)
|
||||||
|
{
|
||||||
|
$isActive = filter_var(
|
||||||
|
$passedIsActive,
|
||||||
|
FILTER_VALIDATE_BOOLEAN,
|
||||||
|
FILTER_NULL_ON_FAILURE
|
||||||
|
);
|
||||||
|
if ($isActive !== null)
|
||||||
|
{
|
||||||
|
$active = $isActive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$degreeProgramShortCode = $this->input->get('degreeProgramShortCode', true);
|
||||||
|
if ($degreeProgramShortCode !== null && $degreeProgramShortCode !== '')
|
||||||
|
{
|
||||||
|
if (!ctype_digit((string)$degreeProgramShortCode))
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'degreeProgramShortCode' => 'A valid degree program is required'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$studiengang_kz = $degreeProgramShortCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
$passedSemester = $this->input->get('semester', true);
|
||||||
|
if ($passedSemester !== null && $passedSemester !== '')
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!ctype_digit((string)$passedSemester)
|
||||||
|
|| (int)$passedSemester < 1
|
||||||
|
|| (int)$passedSemester > 8
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'semester' => 'A valid semester is required'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$semester = filter_var($passedSemester, FILTER_VALIDATE_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news");
|
||||||
|
|
||||||
|
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, false, $titel, true, $sichtbar, $page, $page_size, $sprache, true, $entitledDegreePrograms, $active);
|
||||||
|
$news = $this->getDataOrTerminateWithError($news);
|
||||||
|
|
||||||
|
$this->addMeta('row_count', $news["row_count"] ?? 0);
|
||||||
|
$this->terminateWithSuccess($news["content"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns one news entry with all available language versions for editing.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function getNewsItem($newsId)
|
||||||
|
{
|
||||||
|
if (!is_numeric($newsId) || (int)$newsId < 1)
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'newsId' => 'A valid news ID is required'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newsId = (int)$newsId;
|
||||||
|
$newsResult = $this->NewsModel->load($newsId);
|
||||||
|
|
||||||
|
if (isError($newsResult))
|
||||||
|
{
|
||||||
|
$this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasData($newsResult))
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'newsId' => 'The requested news entry does not exist'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$news = current(getData($newsResult));
|
||||||
|
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news");
|
||||||
|
|
||||||
|
if (!in_array($news->studiengang_kz, $entitledDegreePrograms)) {
|
||||||
|
$this->terminateWithError('You are not entitled to access this news entry', self::ERROR_TYPE_PERMISSION);
|
||||||
|
}
|
||||||
|
$translationsResult = $this->ContentspracheModel->loadWhere([
|
||||||
|
'content_id' => $news->content_id
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isError($translationsResult))
|
||||||
|
{
|
||||||
|
$this->terminateWithError(getError($translationsResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$languages = [];
|
||||||
|
|
||||||
|
foreach ((array)getData($translationsResult) as $translation)
|
||||||
|
{
|
||||||
|
$languages[$translation->sprache] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$translations = [];
|
||||||
|
|
||||||
|
foreach (array_keys($languages) as $language)
|
||||||
|
{
|
||||||
|
$contentResult = $this->ContentModel->getContent(
|
||||||
|
$news->content_id,
|
||||||
|
$language,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isError($contentResult))
|
||||||
|
{
|
||||||
|
$this->terminateWithError(getError($contentResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = getData($contentResult);
|
||||||
|
$document = new DOMDocument('1.0', 'UTF-8');
|
||||||
|
|
||||||
|
if ($content->content !== '' && $document->loadXML($content->content) === false)
|
||||||
|
{
|
||||||
|
$this->terminateWithError(
|
||||||
|
'The stored news content is invalid',
|
||||||
|
self::ERROR_TYPE_GENERAL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$author = $document->getElementsByTagName('verfasser')->item(0);
|
||||||
|
$title = $document->getElementsByTagName('betreff')->item(0);
|
||||||
|
$text = $document->getElementsByTagName('text')->item(0);
|
||||||
|
|
||||||
|
$translations[] = [
|
||||||
|
'language' => $language,
|
||||||
|
'author' => $author ? $author->nodeValue : '',
|
||||||
|
'title' => $title ? $title->nodeValue : '',
|
||||||
|
'text' => $text ? str_replace('dms.php', APP_ROOT . 'cms/dms.php', $text->nodeValue) : '',
|
||||||
|
'isPublished' => (bool)$content->sichtbar
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->terminateWithSuccess([
|
||||||
|
'newsId' => (int)$news->news_id,
|
||||||
|
'visibleFrom' => $news->datum,
|
||||||
|
'visibleTo' => $news->datum_bis,
|
||||||
|
'degreeProgramShortCode' => $news->studiengang_kz,
|
||||||
|
'semester' => $news->semester,
|
||||||
|
'translations' => $translations
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a news entry and all supplied translations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function storeNewsItem()
|
||||||
|
{
|
||||||
|
$this->load->library('form_validation');
|
||||||
|
$postData = $this->input->post(null, true);
|
||||||
|
$this->form_validation->set_data(is_array($postData) ? $postData : []);
|
||||||
|
$this->form_validation->set_rules('visibleFrom', 'Visible from', 'required|is_valid_date');
|
||||||
|
$this->form_validation->set_rules('visibleTo', 'Visible to', 'is_valid_date');
|
||||||
|
$this->form_validation->set_rules(
|
||||||
|
'degreeProgramShortCode',
|
||||||
|
'Degree program',
|
||||||
|
'required|is_natural'
|
||||||
|
);
|
||||||
|
$this->form_validation->set_rules(
|
||||||
|
'semester',
|
||||||
|
'Semester',
|
||||||
|
'is_natural_no_zero|less_than_equal_to[8]'
|
||||||
|
);
|
||||||
|
|
||||||
|
$errors = $this->form_validation->run() === false
|
||||||
|
? $this->form_validation->error_array()
|
||||||
|
: [];
|
||||||
|
$visibleFrom = $this->input->post('visibleFrom', true);
|
||||||
|
$visibleTo = $this->input->post('visibleTo', true);
|
||||||
|
$translations = $this->input->post('translations', true);
|
||||||
|
|
||||||
|
if (
|
||||||
|
is_string($visibleFrom)
|
||||||
|
&& is_string($visibleTo)
|
||||||
|
&& $visibleTo !== ''
|
||||||
|
&& is_valid_date($visibleFrom)
|
||||||
|
&& is_valid_date($visibleTo)
|
||||||
|
&& new DateTime($visibleTo) < new DateTime($visibleFrom)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$errors['visibleTo'] = 'Visible to must not be before Visible from';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($translations) || count($translations) === 0)
|
||||||
|
{
|
||||||
|
$errors['translations'] = 'At least one translation is required';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ($translations as $index => $translation)
|
||||||
|
{
|
||||||
|
if (!is_array($translation))
|
||||||
|
{
|
||||||
|
$errors['translations.' . $index] = 'Each translation must be an object';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($errors) > 0)
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors($errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$this->_ensureUserIsEntitledForDegreeProgram($this->input->post('degreeProgramShortCode', true));
|
||||||
|
|
||||||
|
$degreeProgramShortCode = $this->input->post('degreeProgramShortCode', true);
|
||||||
|
$semester = $this->input->post('semester', true);
|
||||||
|
$semester = $semester === '' ? null : $semester;
|
||||||
|
$visibleTo = $visibleTo === '' ? null : $visibleTo;
|
||||||
|
|
||||||
|
$studiengang = $this->_getStudiengang($degreeProgramShortCode);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'studiengang_kz' => $degreeProgramShortCode,
|
||||||
|
'semester' => $semester,
|
||||||
|
'datum' => $visibleFrom,
|
||||||
|
'datum_bis' => $visibleTo,
|
||||||
|
'translations' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($translations as $index => $translation)
|
||||||
|
{
|
||||||
|
$data['translations'][$index] = [
|
||||||
|
'sprache' => $translation['language'] ?? '',
|
||||||
|
'verfasser' => $translation['author'] ?? '',
|
||||||
|
'betreff' => $translation['title'] ?? '',
|
||||||
|
'text' => $translation['text'] ?? '',
|
||||||
|
'sichtbar' => isset($translation['isPublished']) ? (bool)$translation['isPublished'] : false
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_validateLanguages($data['translations']);
|
||||||
|
|
||||||
|
foreach ($data['translations'] as $index => $translation)
|
||||||
|
{
|
||||||
|
$data['translations'][$index]['content'] = $this->_buildNewsXml($translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
$uid = getAuthUID();
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
if ($this->db->trans_begin() === false)
|
||||||
|
{
|
||||||
|
$this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentResult = $this->ContentModel->insert([
|
||||||
|
'template_kurzbz' => 'news',
|
||||||
|
'oe_kurzbz' => $studiengang->oe_kurzbz,
|
||||||
|
'aktiv' => true,
|
||||||
|
'menu_open' => false,
|
||||||
|
'insertamum' => $now,
|
||||||
|
'insertvon' => $uid
|
||||||
|
]);
|
||||||
|
$contentId = (int)$this->_getInsertedIdOrRollback($contentResult);
|
||||||
|
|
||||||
|
$newsResult = $this->NewsModel->insert([
|
||||||
|
'uid' => $uid,
|
||||||
|
'studiengang_kz' => $data['studiengang_kz'],
|
||||||
|
'semester' => $data['semester'],
|
||||||
|
'datum' => $data['datum'],
|
||||||
|
'datum_bis' => $data['datum_bis'],
|
||||||
|
'content_id' => $contentId,
|
||||||
|
'insertamum' => $now,
|
||||||
|
'insertvon' => $uid,
|
||||||
|
'updateamum' => $now,
|
||||||
|
'updatevon' => $uid
|
||||||
|
]);
|
||||||
|
$newsId = (int)$this->_getInsertedIdOrRollback($newsResult);
|
||||||
|
|
||||||
|
foreach ($data['translations'] as $translation)
|
||||||
|
{
|
||||||
|
$translation['content'] = $this->formatContentURLsToRelative($translation['content']);
|
||||||
|
|
||||||
|
$translationResult = $this->ContentspracheModel->insert([
|
||||||
|
'sprache' => $translation['sprache'],
|
||||||
|
'content_id' => $contentId,
|
||||||
|
'version' => 1,
|
||||||
|
'sichtbar' => $translation['sichtbar'],
|
||||||
|
'content' => $translation['content'],
|
||||||
|
'titel' => $translation['betreff'],
|
||||||
|
'insertamum' => $now,
|
||||||
|
'insertvon' => $uid,
|
||||||
|
'updateamum' => $now,
|
||||||
|
'updatevon' => $uid
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->_getInsertedIdOrRollback($translationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newsItemResult = $this->NewsModel->load($newsId);
|
||||||
|
$newsItem = $this->getDataOrTerminateWithError($newsItemResult);
|
||||||
|
$newsItem = $newsItem[0];
|
||||||
|
|
||||||
|
$this->_sendNewsItemTranslationRequiredMail(
|
||||||
|
$newsItem
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($this->db->trans_status() === false)
|
||||||
|
{
|
||||||
|
$error = $this->db->error();
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError($error, self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->trans_commit() === false)
|
||||||
|
{
|
||||||
|
$error = $this->db->error();
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError($error, self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($newsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a news entry and its supplied translations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function updateNewsItem($newsId)
|
||||||
|
{
|
||||||
|
if (!is_numeric($newsId) || (int)$newsId < 1)
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'newsId' => 'A valid news ID is required'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newsId = (int)$newsId;
|
||||||
|
$existingNewsResult = $this->NewsModel->load($newsId);
|
||||||
|
|
||||||
|
if (isError($existingNewsResult))
|
||||||
|
{
|
||||||
|
$this->terminateWithError(getError($existingNewsResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasData($existingNewsResult))
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'newsId' => 'The requested news entry does not exist'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingNews = current(getData($existingNewsResult));
|
||||||
|
|
||||||
|
$this->_ensureUserIsEntitledForDegreeProgram($existingNews->studiengang_kz);
|
||||||
|
|
||||||
|
$this->load->library('form_validation');
|
||||||
|
$postData = $this->input->post(null, true);
|
||||||
|
$this->form_validation->set_data(is_array($postData) ? $postData : []);
|
||||||
|
$this->form_validation->set_rules('visibleFrom', 'Visible from', 'required|is_valid_date');
|
||||||
|
$this->form_validation->set_rules('visibleTo', 'Visible to', 'is_valid_date');
|
||||||
|
$this->form_validation->set_rules(
|
||||||
|
'degreeProgramShortCode',
|
||||||
|
'Degree program',
|
||||||
|
'required|is_natural'
|
||||||
|
);
|
||||||
|
$this->form_validation->set_rules(
|
||||||
|
'semester',
|
||||||
|
'Semester',
|
||||||
|
'is_natural_no_zero|less_than_equal_to[8]'
|
||||||
|
);
|
||||||
|
|
||||||
|
$errors = $this->form_validation->run() === false
|
||||||
|
? $this->form_validation->error_array()
|
||||||
|
: [];
|
||||||
|
$visibleFrom = $this->input->post('visibleFrom', true);
|
||||||
|
$visibleTo = $this->input->post('visibleTo', true);
|
||||||
|
$translations = $this->input->post('translations', true);
|
||||||
|
|
||||||
|
if (
|
||||||
|
is_string($visibleFrom)
|
||||||
|
&& is_string($visibleTo)
|
||||||
|
&& $visibleTo !== ''
|
||||||
|
&& is_valid_date($visibleFrom)
|
||||||
|
&& is_valid_date($visibleTo)
|
||||||
|
&& new DateTime($visibleTo) < new DateTime($visibleFrom)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$errors['visibleTo'] = 'Visible to must not be before Visible from';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($translations) || count($translations) === 0)
|
||||||
|
{
|
||||||
|
$errors['translations'] = 'At least one translation is required';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ($translations as $index => $translation)
|
||||||
|
{
|
||||||
|
if (!is_array($translation))
|
||||||
|
{
|
||||||
|
$errors['translations.' . $index] = 'Each translation must be an object';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($errors) > 0)
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors($errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
$degreeProgramShortCode = $this->input->post('degreeProgramShortCode', true);
|
||||||
|
$semester = $this->input->post('semester', true);
|
||||||
|
$semester = $semester === '' ? null : $semester;
|
||||||
|
$visibleTo = $visibleTo === '' ? null : $visibleTo;
|
||||||
|
|
||||||
|
$studiengang = $this->_getStudiengang($degreeProgramShortCode);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'studiengang_kz' => $degreeProgramShortCode,
|
||||||
|
'semester' => $semester,
|
||||||
|
'datum' => $visibleFrom,
|
||||||
|
'datum_bis' => $visibleTo,
|
||||||
|
'translations' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($translations as $index => $translation)
|
||||||
|
{
|
||||||
|
$data['translations'][$index] = [
|
||||||
|
'sprache' => $translation['language'] ?? '',
|
||||||
|
'verfasser' => $translation['author'] ?? '',
|
||||||
|
'betreff' => $translation['title'] ?? '',
|
||||||
|
'text' => $translation['text'] ?? '',
|
||||||
|
'sichtbar' => isset($translation['isPublished']) ? (bool)$translation['isPublished'] : false
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_validateLanguages($data['translations']);
|
||||||
|
|
||||||
|
foreach ($data['translations'] as $index => $translation)
|
||||||
|
{
|
||||||
|
$data['translations'][$index]['content'] = $this->_buildNewsXml($translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
$uid = getAuthUID();
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
if ($this->db->trans_begin() === false)
|
||||||
|
{
|
||||||
|
$this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentResult = $this->ContentModel->update($existingNews->content_id, [
|
||||||
|
'oe_kurzbz' => $studiengang->oe_kurzbz,
|
||||||
|
'updateamum' => $now,
|
||||||
|
'updatevon' => $uid
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isError($contentResult))
|
||||||
|
{
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError(getError($contentResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newsResult = $this->NewsModel->update($newsId, [
|
||||||
|
'studiengang_kz' => $data['studiengang_kz'],
|
||||||
|
'semester' => $data['semester'],
|
||||||
|
'datum' => $data['datum'],
|
||||||
|
'datum_bis' => $data['datum_bis'],
|
||||||
|
'updateamum' => $now,
|
||||||
|
'updatevon' => $uid
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isError($newsResult))
|
||||||
|
{
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingTranslationsResult = $this->ContentspracheModel->loadWhere([
|
||||||
|
'content_id' => $existingNews->content_id
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isError($existingTranslationsResult))
|
||||||
|
{
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError(getError($existingTranslationsResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingTranslations = [];
|
||||||
|
|
||||||
|
foreach ((array)getData($existingTranslationsResult) as $existingTranslation)
|
||||||
|
{
|
||||||
|
$language = $existingTranslation->sprache;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isset($existingTranslations[$language])
|
||||||
|
|| (int)$existingTranslation->version > (int)$existingTranslations[$language]->version
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$existingTranslations[$language] = $existingTranslation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($data['translations'] as $translation)
|
||||||
|
{
|
||||||
|
$translation['content'] = $this->formatContentURLsToRelative($translation['content']);
|
||||||
|
$translationData = [
|
||||||
|
'sichtbar' => $translation['sichtbar'],
|
||||||
|
'content' => $translation['content'],
|
||||||
|
'titel' => $translation['betreff'],
|
||||||
|
'updateamum' => $now,
|
||||||
|
'updatevon' => $uid
|
||||||
|
];
|
||||||
|
|
||||||
|
$existingTranslation = $existingTranslations[$translation['sprache']] ?? null;
|
||||||
|
|
||||||
|
if ($existingTranslation)
|
||||||
|
{
|
||||||
|
$translationResult = $this->ContentspracheModel->update(
|
||||||
|
$existingTranslation->contentsprache_id,
|
||||||
|
$translationData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$translationResult = $this->ContentspracheModel->insert([
|
||||||
|
'sprache' => $translation['sprache'],
|
||||||
|
'content_id' => $existingNews->content_id,
|
||||||
|
'version' => 1,
|
||||||
|
'insertamum' => $now,
|
||||||
|
'insertvon' => $uid
|
||||||
|
] + $translationData);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_getInsertedIdOrRollback($translationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->trans_status() === false)
|
||||||
|
{
|
||||||
|
$error = $this->db->error();
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError($error, self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->trans_commit() === false)
|
||||||
|
{
|
||||||
|
$error = $this->db->error();
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError($error, self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($newsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes one news entry and its linked content.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteNewsItem($newsId)
|
||||||
|
{
|
||||||
|
if (!is_numeric($newsId) || (int)$newsId < 1)
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'newsId' => 'A valid news ID is required'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newsId = (int)$newsId;
|
||||||
|
$newsResult = $this->NewsModel->load($newsId);
|
||||||
|
|
||||||
|
if (isError($newsResult))
|
||||||
|
{
|
||||||
|
$this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasData($newsResult))
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'newsId' => 'The requested news entry does not exist'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$news = current(getData($newsResult));
|
||||||
|
|
||||||
|
|
||||||
|
$this->_ensureUserIsEntitledForDegreeProgram($news->studiengang_kz);
|
||||||
|
|
||||||
|
if ($this->db->trans_begin() === false)
|
||||||
|
{
|
||||||
|
$this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newsDeleteResult = $this->NewsModel->delete($newsId);
|
||||||
|
|
||||||
|
if (isError($newsDeleteResult))
|
||||||
|
{
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError(getError($newsDeleteResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->where('content_id', $news->content_id);
|
||||||
|
|
||||||
|
if ($this->db->delete('campus.tbl_contentsprache') === false)
|
||||||
|
{
|
||||||
|
$error = $this->db->error();
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError($error, self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentDeleteResult = $this->ContentModel->delete($news->content_id);
|
||||||
|
|
||||||
|
if (isError($contentDeleteResult))
|
||||||
|
{
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError(getError($contentDeleteResult), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->trans_status() === false)
|
||||||
|
{
|
||||||
|
$error = $this->db->error();
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError($error, self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->trans_commit() === false)
|
||||||
|
{
|
||||||
|
$error = $this->db->error();
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError($error, self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($newsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Private methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the requested Studiengang or reports a validation error.
|
||||||
|
*
|
||||||
|
* @param integer $studiengangKz
|
||||||
|
* @return stdClass
|
||||||
|
*/
|
||||||
|
private function _getStudiengang($studiengangKz)
|
||||||
|
{
|
||||||
|
$result = $this->StudiengangModel->load($studiengangKz);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
{
|
||||||
|
$this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasData($result))
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors([
|
||||||
|
'studiengang_kz' => 'The requested Studiengang does not exist'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$studiengaenge = getData($result);
|
||||||
|
|
||||||
|
return current($studiengaenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that all supplied languages exist in tbl_sprache.
|
||||||
|
*
|
||||||
|
* @param array $translations
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function _validateLanguages($translations)
|
||||||
|
{
|
||||||
|
$languages = [];
|
||||||
|
|
||||||
|
foreach ($translations as $translation)
|
||||||
|
{
|
||||||
|
$languages[] = $translation['sprache'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->SpracheModel->loadMultiple($languages);
|
||||||
|
|
||||||
|
if (isError($result))
|
||||||
|
{
|
||||||
|
$this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingLanguages = [];
|
||||||
|
|
||||||
|
foreach ((array)getData($result) as $language)
|
||||||
|
{
|
||||||
|
$existingLanguages[$language->sprache] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
foreach ($translations as $index => $translation)
|
||||||
|
{
|
||||||
|
if (!isset($existingLanguages[$translation['sprache']]))
|
||||||
|
{
|
||||||
|
$errors['translations.' . $index . '.sprache'] = 'The requested language does not exist';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($errors) > 0)
|
||||||
|
{
|
||||||
|
$this->terminateWithValidationErrors($errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _ensureUserIsEntitledForDegreeProgram($degreeProgramShortCode)
|
||||||
|
{
|
||||||
|
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news:w");
|
||||||
|
if (!is_array($entitledDegreePrograms))
|
||||||
|
{
|
||||||
|
$this->terminateWithError('Failed to retrieve entitled degree programs for the user', self::ERROR_TYPE_GENERAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($degreeProgramShortCode, $entitledDegreePrograms)) {
|
||||||
|
$this->terminateWithError("User is not entitled for the specified degree program");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns an inserted identifier or rolls the transaction back on error.
|
||||||
|
*
|
||||||
|
* @param stdClass $result
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function _getInsertedIdOrRollback($result)
|
||||||
|
{
|
||||||
|
if (isError($result))
|
||||||
|
{
|
||||||
|
$this->db->trans_rollback();
|
||||||
|
$this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getData($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the XML format consumed by existing news readers.
|
||||||
|
*
|
||||||
|
* @param array $translation
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function _buildNewsXml($translation)
|
||||||
|
{
|
||||||
|
$document = new DOMDocument('1.0', 'UTF-8');
|
||||||
|
$news = $document->createElement('news');
|
||||||
|
$document->appendChild($news);
|
||||||
|
|
||||||
|
$this->_appendCdataElement($document, $news, 'verfasser', $translation['verfasser']);
|
||||||
|
$this->_appendCdataElement($document, $news, 'betreff', $translation['betreff']);
|
||||||
|
$this->_appendCdataElement($document, $news, 'text', $translation['text']);
|
||||||
|
|
||||||
|
return $document->saveXML($news);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a CDATA element and safely preserves CDATA terminators.
|
||||||
|
*
|
||||||
|
* @param DOMDocument $document
|
||||||
|
* @param DOMElement $parent
|
||||||
|
* @param string $name
|
||||||
|
* @param string $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function _appendCdataElement($document, $parent, $name, $value)
|
||||||
|
{
|
||||||
|
$element = $document->createElement($name);
|
||||||
|
$parts = explode(']]>', $value);
|
||||||
|
$lastIndex = count($parts) - 1;
|
||||||
|
|
||||||
|
foreach ($parts as $index => $part)
|
||||||
|
{
|
||||||
|
if ($index > 0)
|
||||||
|
{
|
||||||
|
$part = '>' . $part;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($index < $lastIndex)
|
||||||
|
{
|
||||||
|
$part .= ']]';
|
||||||
|
}
|
||||||
|
|
||||||
|
$element->appendChild($document->createCDATASection($part));
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent->appendChild($element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _sendNewsItemTranslationRequiredMail($newsItem)
|
||||||
|
{
|
||||||
|
$translatorsResult = $this->BenutzerfunktionModel->getBenutzerFunktionen("translate", $newsItem->studiengang_kz);
|
||||||
|
$translators = $this->getDataOrTerminateWithError($translatorsResult);
|
||||||
|
|
||||||
|
$newsItemContentResult = $this->ContentspracheModel->loadWhere([
|
||||||
|
'content_id' => $newsItem->content_id,
|
||||||
|
'sprache' => 'German'
|
||||||
|
]);
|
||||||
|
$newsItemContentEntry = getData($newsItemContentResult)[0] ?? null;
|
||||||
|
if ($newsItemContentEntry) {
|
||||||
|
$newsItem->sichtbar = $newsItemContentEntry->sichtbar ?? false;
|
||||||
|
$newsItem->content = $newsItemContentEntry->content;
|
||||||
|
}
|
||||||
|
$content = getData(
|
||||||
|
$this->cmslib->createNewsContent(
|
||||||
|
$this->cmslib->createNewsWrapper($newsItem, true),
|
||||||
|
false,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($translators as $translator) {
|
||||||
|
sendSanchoMail(
|
||||||
|
"Sancho_Mail_News_Translation_Req",
|
||||||
|
[
|
||||||
|
"newsItemUrl" => APP_ROOT . "cis.php/CisVue/Cms/newsAdministration?newsId=" . $newsItem->news_id,
|
||||||
|
"newsItemAuthor" => $newsItem->verfasser,
|
||||||
|
"newsItemSubject" => $newsItem->betreff,
|
||||||
|
"newsItemContent" => $content
|
||||||
|
],
|
||||||
|
$translator->uid . "@" . DOMAIN,
|
||||||
|
"Übersetzung erforderlich / Translation required"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatContentURLsToRelative($content)
|
||||||
|
{
|
||||||
|
$content = preg_replace(
|
||||||
|
'#https?://[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i',
|
||||||
|
'$1',
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
|
||||||
|
$content = preg_replace(
|
||||||
|
'#(?:\.\./)+[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i',
|
||||||
|
'$1',
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
|
||||||
|
$content = preg_replace(
|
||||||
|
'#/[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i',
|
||||||
|
'$1',
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ class Studgang extends FHCAPI_Controller
|
|||||||
{
|
{
|
||||||
parent::__construct([
|
parent::__construct([
|
||||||
'getStudiengangInfo'=> self::PERM_LOGGED,
|
'getStudiengangInfo'=> self::PERM_LOGGED,
|
||||||
|
'getDegreePrograms'=> self::PERM_LOGGED,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
@@ -43,6 +43,22 @@ class Studgang extends FHCAPI_Controller
|
|||||||
//------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
// Public methods
|
// Public methods
|
||||||
|
|
||||||
|
public function getDegreePrograms() {
|
||||||
|
$entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news");
|
||||||
|
if (!$entitledDegreePrograms) {
|
||||||
|
$this->terminateWithError("User is not entitled for any degree program");
|
||||||
|
}
|
||||||
|
|
||||||
|
$activeDegreePrograms = $this->StudiengangModel->getDegreePrograms();
|
||||||
|
$activeDegreePrograms= $this->getDataOrTerminateWithError($activeDegreePrograms);
|
||||||
|
|
||||||
|
$activeDegreePrograms = array_values(array_filter($activeDegreePrograms, function($degreeProgram) use ($entitledDegreePrograms) {
|
||||||
|
return in_array($degreeProgram->studiengang_kz, $entitledDegreePrograms);
|
||||||
|
}));
|
||||||
|
|
||||||
|
$this->terminateWithSuccess($activeDegreePrograms);
|
||||||
|
}
|
||||||
|
|
||||||
public function getStudiengangInfo(){
|
public function getStudiengangInfo(){
|
||||||
$isMitarbeiter = $this->MitarbeiterModel->isMitarbeiter(getAuthUID());
|
$isMitarbeiter = $this->MitarbeiterModel->isMitarbeiter(getAuthUID());
|
||||||
$isMitarbeiter = $this->getDataOrTerminateWithError($isMitarbeiter);
|
$isMitarbeiter = $this->getDataOrTerminateWithError($isMitarbeiter);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ if (!defined('BASEPATH'))
|
|||||||
exit('No direct script access allowed');
|
exit('No direct script access allowed');
|
||||||
|
|
||||||
use \DateTime as DateTime;
|
use \DateTime as DateTime;
|
||||||
|
use \DateInterval as DateInterval;
|
||||||
use \DOMDocument as DOMDocument;
|
use \DOMDocument as DOMDocument;
|
||||||
use \XSLTProcessor as XSLTProcessor;
|
use \XSLTProcessor as XSLTProcessor;
|
||||||
|
|
||||||
@@ -141,63 +142,6 @@ class CmsLib
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param stdClass $stg_obj
|
|
||||||
*
|
|
||||||
* @return stdClass
|
|
||||||
*/
|
|
||||||
protected function getNewsExtras($stg_obj, $semester)
|
|
||||||
{
|
|
||||||
$this->ci->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
|
|
||||||
|
|
||||||
$stg_ltg = $this->ci->StudiengangModel->getLeitungDetailed($stg_obj->studiengang_kz);
|
|
||||||
if (isError($stg_ltg))
|
|
||||||
return $stg_ltg;
|
|
||||||
$stg_ltg = getData($stg_ltg) ?: [];
|
|
||||||
|
|
||||||
$gf_ltg = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('gLtg', $stg_obj->oe_kurzbz);
|
|
||||||
if (isError($gf_ltg))
|
|
||||||
return $gf_ltg;
|
|
||||||
$gf_ltg = getData($gf_ltg) ?: [];
|
|
||||||
|
|
||||||
$stv_ltg = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('stvLtg', $stg_obj->oe_kurzbz);
|
|
||||||
if (isError($stv_ltg))
|
|
||||||
return $stv_ltg;
|
|
||||||
$stv_ltg = getData($stv_ltg) ?: [];
|
|
||||||
|
|
||||||
$ass = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('ass', $stg_obj->oe_kurzbz);
|
|
||||||
if (isError($ass))
|
|
||||||
return $ass;
|
|
||||||
$ass = getData($ass) ?: [];
|
|
||||||
|
|
||||||
$hochschulvertr = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('hsv');
|
|
||||||
if (isError($hochschulvertr))
|
|
||||||
return $hochschulvertr;
|
|
||||||
$hochschulvertr = getData($hochschulvertr) ?: [];
|
|
||||||
|
|
||||||
$stdv = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('stdv', $stg_obj->oe_kurzbz);
|
|
||||||
if (isError($stdv))
|
|
||||||
return $stdv;
|
|
||||||
$stdv = getData($stdv) ?: [];
|
|
||||||
|
|
||||||
$jahrgangsvertr = $this->ci->BenutzerfunktionModel->getBenutzerFunktionenDetailed('jgv', $stg_obj->oe_kurzbz, $semester);
|
|
||||||
if (isError($jahrgangsvertr))
|
|
||||||
return $jahrgangsvertr;
|
|
||||||
$jahrgangsvertr = getData($jahrgangsvertr) ?: [];
|
|
||||||
|
|
||||||
return success($this->ci->load->view('Cis/Cms/News/Xml/NewsExtras', [
|
|
||||||
'studiengang' => $stg_obj,
|
|
||||||
'semester' => $semester,
|
|
||||||
'stg_ltg' => $stg_ltg,
|
|
||||||
'gf_ltg' => $gf_ltg,
|
|
||||||
'stv_ltg' => $stv_ltg,
|
|
||||||
'ass' => $ass,
|
|
||||||
'hochschulvertr' => $hochschulvertr,
|
|
||||||
'stdv' => $stdv,
|
|
||||||
'jahrgangsvertr' => $jahrgangsvertr
|
|
||||||
], true));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $studiengang_kz
|
* @param string $studiengang_kz
|
||||||
* @param string $semester
|
* @param string $semester
|
||||||
@@ -233,64 +177,102 @@ class CmsLib
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true, $page = 1, $page_size = 10, $sprache)
|
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true, $page = 1, $page_size = 10, $sprache, $filterForDegreePrograms = false, $allowedDegreePrograms = [], $active = true)
|
||||||
|
|
||||||
{
|
{
|
||||||
$this->ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
$this->ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
list($studiengang_kz, $semester) = $this->getStgAndSem($studiengang_kz, $semester);
|
list($studiengang_kz, $semester) = $this->getStgAndSem($studiengang_kz, $semester);
|
||||||
$all = $edit;
|
|
||||||
|
|
||||||
$xml = '<?xml version="1.0" encoding="UTF-8"?><content>';
|
|
||||||
|
|
||||||
$this->ci->load->model('content/News_model', 'NewsModel');
|
$this->ci->load->model('content/News_model', 'NewsModel');
|
||||||
$news = $this->ci->NewsModel->getNewsWithContent($sprache, $studiengang_kz, $semester, null, $sichtbar, 0, $page, $page_size, $all, $mischen);
|
|
||||||
|
|
||||||
if (isError($news))
|
|
||||||
return $news;
|
|
||||||
|
|
||||||
$news = getData($news);
|
|
||||||
|
|
||||||
|
$news = $this->ci->NewsModel->getNewsWithContent($sprache, $studiengang_kz, $semester, null, $sichtbar, $page, $page_size, $active, $mischen, $filterForDegreePrograms, $allowedDegreePrograms);
|
||||||
|
$news = getData($news) ?? [];
|
||||||
|
$newsWrappers = [];
|
||||||
|
|
||||||
foreach ($news as $newsobj) {
|
foreach ($news as $newsobj) {
|
||||||
if ($studiengang_kz && $edit && !$newsobj->studiengang_kz)
|
if ($studiengang_kz && $edit && !$newsobj->studiengang_kz)
|
||||||
continue;
|
continue;
|
||||||
$date = new DateTime($newsobj->datum);
|
|
||||||
$datum = '<datum><![CDATA[' . $date->format('d.m.Y') . ']]></datum>';
|
$newsWrappers[] = $this->createNewsWrapper($newsobj, $edit);
|
||||||
$datum .= '<datumdetail><![CDATA[' . $date->format('Y-m-d H:i') . ']]></datumdetail>';
|
|
||||||
$id = $edit ? '<news_id><![CDATA[' . $newsobj->news_id . ']]></news_id>' : '';
|
|
||||||
$xml .= "<newswrapper>" . $newsobj->content . $datum . $id . "</newswrapper>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if ($studiengang_kz != 0) {
|
$content = $this->createNewsContent($newsWrappers, $infoscreen, $titel);
|
||||||
$stg_obj = $this->ci->StudiengangModel->load($studiengang_kz);
|
if (isError($content))
|
||||||
if (isError($stg_obj))
|
return $content;
|
||||||
return $stg_obj;
|
|
||||||
$stg_obj = current(getData($stg_obj) ?: []);
|
|
||||||
|
|
||||||
if ($stg_obj) {
|
return success([
|
||||||
if (!$edit && !$infoscreen) {
|
"content" => getData($content),
|
||||||
$extras = $this->getNewsExtras($stg_obj, $semester);
|
"row_count" => $news[0]->row_count ?? 0
|
||||||
if (isError($extras))
|
]);
|
||||||
return $extras;
|
}
|
||||||
$xml .= getData($extras);
|
|
||||||
}
|
/**
|
||||||
$xml .= '<studiengang_bezeichnung><![CDATA[' . $stg_obj->bezeichnung . ']]></studiengang_bezeichnung>';
|
* Creates a newswrapper XML element from a news item.
|
||||||
}
|
*
|
||||||
} */
|
* @param stdClass $newsobj
|
||||||
|
* @param boolean $edit
|
||||||
|
*
|
||||||
|
* @return DOMElement
|
||||||
|
*/
|
||||||
|
public function createNewsWrapper($newsobj, $edit)
|
||||||
|
{
|
||||||
|
$id = $edit ? '<news_id><![CDATA[' . $newsobj->news_id . ']]></news_id>' : '';
|
||||||
|
$isPublished = $edit ? '<is_published><![CDATA[' . ($newsobj->sichtbar ? 'true' : 'false') . ']]></is_published>' : '';
|
||||||
|
|
||||||
|
$date = new DateTime($newsobj->datum);
|
||||||
|
$datum = '<datum><![CDATA[' . $date->format('d.m.Y') . ']]></datum>';
|
||||||
|
$datum .= '<datumdetail><![CDATA[' . $date->format('Y-m-d H:i') . ']]></datumdetail>';
|
||||||
|
|
||||||
|
$hasVisibleToDate = isset($newsobj->datum_bis) && $newsobj->datum_bis !== '';
|
||||||
|
$datumTo = $hasVisibleToDate
|
||||||
|
? new DateTime($newsobj->datum_bis)
|
||||||
|
: (clone $date)->add(new DateInterval('P' . MAXNEWSALTER . 'D'));
|
||||||
|
if ($hasVisibleToDate)
|
||||||
|
$datumTo->setTime(23, 59, 59);
|
||||||
|
|
||||||
|
$now = new DateTime();
|
||||||
|
$isActive = '<is_active><![CDATA[' . ($datumTo >= $now ? 'true' : 'false') . ']]></is_active>';
|
||||||
|
|
||||||
|
$newsWrapper = new DOMDocument();
|
||||||
|
$newsWrapper->loadXML('<newswrapper>' . $newsobj->content . $datum . $id . $isPublished . $isActive . '</newswrapper>');
|
||||||
|
|
||||||
|
return $newsWrapper->documentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates rendered news content from one or more newswrapper XML elements.
|
||||||
|
*
|
||||||
|
* @param DOMElement|array $newsWrappers
|
||||||
|
* @param boolean $infoscreen
|
||||||
|
* @param string $titel
|
||||||
|
*
|
||||||
|
* @return stdClass
|
||||||
|
*/
|
||||||
|
public function createNewsContent($newsWrappers, $infoscreen, $titel)
|
||||||
|
{
|
||||||
|
if (!is_array($newsWrappers))
|
||||||
|
$newsWrappers = [$newsWrappers];
|
||||||
|
|
||||||
|
$XML = new DOMDocument('1.0', 'UTF-8');
|
||||||
|
$contentElement = $XML->createElement('content');
|
||||||
|
$XML->appendChild($contentElement);
|
||||||
|
|
||||||
|
foreach ($newsWrappers as $newsWrapper)
|
||||||
|
$contentElement->appendChild($XML->importNode($newsWrapper, true));
|
||||||
|
|
||||||
if ($titel != '') {
|
if ($titel != '') {
|
||||||
$xml .= '<news_titel>' . $titel . '</news_titel>';
|
$titleElement = $XML->createElement('news_titel');
|
||||||
|
$titleElement->appendChild($XML->createTextNode($titel));
|
||||||
|
$contentElement->appendChild($titleElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
$xml .= '</content>';
|
|
||||||
|
|
||||||
//XSLT Vorlage laden
|
//XSLT Vorlage laden
|
||||||
$template = $this->ci->TemplateModel->load($infoscreen ? 'news_infoscreen' : 'news');
|
$template = $this->ci->TemplateModel->load($infoscreen ? 'news_infoscreen' : 'news');
|
||||||
if (isError($template))
|
if (isError($template))
|
||||||
return $template;
|
return $template;
|
||||||
$template = current(getData($template));
|
$template = current(getData($template));
|
||||||
|
|
||||||
$XML = new DOMDocument();
|
|
||||||
$XML->loadXML($xml);
|
|
||||||
|
|
||||||
$xsltemplate = new DOMDocument();
|
$xsltemplate = new DOMDocument();
|
||||||
$xsltemplate->loadXML($template->xslt_xhtml_c4);
|
$xsltemplate->loadXML($template->xslt_xhtml_c4);
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,16 @@ class News_model extends DB_Model
|
|||||||
public function getAll($limit = null)
|
public function getAll($limit = null)
|
||||||
{
|
{
|
||||||
$this->addJoin("campus.tbl_content","content_id");
|
$this->addJoin("campus.tbl_content","content_id");
|
||||||
|
|
||||||
return $this->execReadOnlyQuery('
|
return $this->execReadOnlyQuery('
|
||||||
SELECT *, TO_CHAR(campus.tbl_news.datum, ?) as datumformatted
|
SELECT *, TO_CHAR(campus.tbl_news.datum, ?) as datumformatted
|
||||||
FROM campus.tbl_news
|
FROM campus.tbl_news
|
||||||
JOIN campus.tbl_content content ON content.content_id = campus.tbl_news.content_id
|
JOIN campus.tbl_content content ON content.content_id = campus.tbl_news.content_id
|
||||||
WHERE
|
WHERE
|
||||||
--text IS NOT NULL AND
|
--text IS NOT NULL AND
|
||||||
datum <= NOW() AND (datum_bis IS NULL OR datum_bis >= now()::date)
|
datum <= NOW() AND (datum_bis >= NOW()::date OR datum_bis IS NULL) AND
|
||||||
ORDER BY datum DESC
|
(NOW()-datum) < interval \'' . MAXNEWSALTER . ' days\'
|
||||||
|
ORDER BY datum DESC ORDER BY campus.tbl_news.updateamum DESC
|
||||||
LIMIT ' . $this->escape($limit)
|
LIMIT ' . $this->escape($limit)
|
||||||
, ['DD/MM/YYYY']);
|
, ['DD/MM/YYYY']);
|
||||||
}
|
}
|
||||||
@@ -48,31 +50,28 @@ class News_model extends DB_Model
|
|||||||
* @param integer | null $semester
|
* @param integer | null $semester
|
||||||
* @param string $fachbereich_kurzbz
|
* @param string $fachbereich_kurzbz
|
||||||
* @param boolean $sichtbar
|
* @param boolean $sichtbar
|
||||||
* @param integer $maxalter
|
|
||||||
* @param integer $page
|
* @param integer $page
|
||||||
* @param integer $page_size
|
* @param integer $page_size
|
||||||
* @param boolean $all
|
* @param boolean $active
|
||||||
* @param boolean $mischen
|
* @param boolean $mischen
|
||||||
*
|
*
|
||||||
* TODO(chris): this is not a good function -> the params are all over the place
|
* TODO(chris): this is not a good function -> the params are all over the place
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected function prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $maxalter = 0, $page = 1, $page_size = 10, $all = false, $mischen = true)
|
protected function prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $page = 1, $page_size = 10, $active = true, $mischen = true, $filterForDegreePrograms = false, $allowedDegreePrograms = [])
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->addSelect(["*", "COUNT(*) OVER() AS row_count"]);
|
||||||
$this->addOrder('datum', 'DESC');
|
$this->addOrder('datum', 'DESC');
|
||||||
|
$this->addOrder('tbl_news.updateamum', 'DESC');
|
||||||
|
|
||||||
$studiengang_kz = trim($studiengang_kz);
|
$studiengang_kz = trim($studiengang_kz);
|
||||||
$fachbereich_kurzbz = trim($fachbereich_kurzbz);
|
$fachbereich_kurzbz = trim($fachbereich_kurzbz);
|
||||||
|
|
||||||
$where = [];
|
$where = [];
|
||||||
if (trim($maxalter) != '0') {
|
|
||||||
$where[] = "(now()-datum) < interval " . $this->db->escape($maxalter) . " days";
|
if ($active) {
|
||||||
|
$where[] = "(datum <= NOW()::date AND (datum_bis >= NOW()::date OR (datum_bis IS NULL AND (NOW()-datum) < interval '" . MAXNEWSALTER . " days')))";
|
||||||
}
|
|
||||||
if (!$all) {
|
|
||||||
$where[] = "datum <= now()";
|
|
||||||
$where[] = "(datum_bis >= now()::date OR datum_bis IS NULL)";
|
|
||||||
}
|
}
|
||||||
if ($fachbereich_kurzbz != '*') {
|
if ($fachbereich_kurzbz != '*') {
|
||||||
if ($fachbereich_kurzbz == '') {
|
if ($fachbereich_kurzbz == '') {
|
||||||
@@ -82,27 +81,54 @@ class News_model extends DB_Model
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($studiengang_kz == '0') {
|
|
||||||
$where[] = "studiengang_kz = " . $this->db->escape($studiengang_kz);
|
|
||||||
|
|
||||||
if ($semester === NULL)
|
$sgAndSemesterCondition = [];
|
||||||
$where[] = "semester IS NULL";
|
|
||||||
elseif ($semester === 0)
|
|
||||||
$where[] = "semester = 0";
|
|
||||||
} elseif ($studiengang_kz != '') {
|
|
||||||
$add = $mischen === true ? " OR (studiengang_kz = 0 AND semester IS NULL)" : "";
|
|
||||||
$where[] = "((studiengang_kz = " . $this->db->escape($studiengang_kz) . " AND semester = " . $this->db->escape($semester) . ") OR (studiengang_kz = " . $this->db->escape($studiengang_kz) . " AND semester = 0) OR (studiengang_kz = 0 AND semester = " . $this->db->escape($semester) . ")" . $add . ")";
|
|
||||||
|
|
||||||
|
if ($studiengang_kz !== '') {
|
||||||
|
$sgAndSemesterCondition[] = 'studiengang_kz = ' . $this->db->escape($studiengang_kz);
|
||||||
}
|
}
|
||||||
|
if ($semester !== null && $semester !== 0) {
|
||||||
|
$sgAndSemesterCondition[] = 'semester = ' . $this->db->escape($semester);
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetedSgAndSemesterCondition = [];
|
||||||
|
|
||||||
|
if (!empty($sgAndSemesterCondition)) {
|
||||||
|
$targetedSgAndSemesterCondition[] = '(' . implode(' AND ', $sgAndSemesterCondition) . ')';
|
||||||
|
}
|
||||||
|
if ($mischen === true) {
|
||||||
|
$targetedSgAndSemesterCondition[] = '(studiengang_kz = 0 AND (semester = 0 OR semester IS NULL))';
|
||||||
|
|
||||||
|
if ($semester !== null && $semester !== 0) {
|
||||||
|
$targetedSgAndSemesterCondition[] = '(studiengang_kz = 0 AND semester = ' . $this->db->escape($semester) . ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$finalSgAndSemesterCondition = implode(' OR ', $targetedSgAndSemesterCondition);
|
||||||
|
|
||||||
|
if ($finalSgAndSemesterCondition !== '') {
|
||||||
|
$where[] = "(" . $finalSgAndSemesterCondition . ")";
|
||||||
|
}
|
||||||
|
|
||||||
$this->addJoin('campus.tbl_contentsprache cs', 'content_id');
|
$this->addJoin('campus.tbl_contentsprache cs', 'content_id');
|
||||||
|
|
||||||
$where[] = "cs.sichtbar = " . ($sichtbar ? "true" : "false");
|
if (is_bool($sichtbar) || $sichtbar === 'true' || $sichtbar === 'false') {
|
||||||
|
$isSichtbar = filter_var($sichtbar, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
$where[] = "cs.sichtbar = " . ($isSichtbar ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
$where[] = "cs.sprache = (CASE WHEN EXISTS(SELECT 1 FROM campus.tbl_contentsprache cs2 WHERE cs2.content_id=" . $this->dbTable . ".content_id AND sprache=" . $this->db->escape($sprache) . ") THEN " . $this->db->escape($sprache) . " ELSE " . $this->db->escape(DEFAULT_LANGUAGE) . " END)";
|
$where[] = "cs.sprache = (CASE WHEN EXISTS(SELECT 1 FROM campus.tbl_contentsprache cs2 WHERE cs2.content_id=" . $this->dbTable . ".content_id AND sprache=" . $this->db->escape($sprache) . ") THEN " . $this->db->escape($sprache) . " ELSE " . $this->db->escape(DEFAULT_LANGUAGE) . " END)";
|
||||||
|
|
||||||
|
|
||||||
$where[] = "cs.version = (SELECT MAX(version) FROM campus.tbl_contentsprache cs3 WHERE cs3.content_id=" . $this->dbTable . ".content_id AND cs3.sprache = (CASE WHEN EXISTS(SELECT 1 FROM campus.tbl_contentsprache cs2 WHERE cs2.content_id=" . $this->dbTable . ".content_id AND sprache=" . $this->db->escape($sprache) . ") THEN " . $this->db->escape($sprache) . " ELSE " . $this->db->escape(DEFAULT_LANGUAGE) . " END))";
|
$where[] = "cs.version = (SELECT MAX(version) FROM campus.tbl_contentsprache cs3 WHERE cs3.content_id=" . $this->dbTable . ".content_id AND cs3.sprache = (CASE WHEN EXISTS(SELECT 1 FROM campus.tbl_contentsprache cs2 WHERE cs2.content_id=" . $this->dbTable . ".content_id AND sprache=" . $this->db->escape($sprache) . ") THEN " . $this->db->escape($sprache) . " ELSE " . $this->db->escape(DEFAULT_LANGUAGE) . " END))";
|
||||||
|
|
||||||
|
if ($filterForDegreePrograms) {
|
||||||
|
if (empty($allowedDegreePrograms)) {
|
||||||
|
$where[] = "1=0";
|
||||||
|
} else {
|
||||||
|
$where[] = "studiengang_kz IN (" . implode(',', array_map([$this->db, 'escape'], $allowedDegreePrograms)) . ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$where = implode(" AND ", $where);
|
$where = implode(" AND ", $where);
|
||||||
|
|
||||||
@@ -110,25 +136,16 @@ class News_model extends DB_Model
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $maxalter = 0, $page = 1, $page_size = 10, $all = false, $mischen = true)
|
public function getNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $page = 1, $page_size = 10, $active = true, $mischen = true, $filterForDegreePrograms = false, $allowedDegreePrograms = [])
|
||||||
{
|
{
|
||||||
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen);
|
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $page, $page_size, $active, $mischen, $filterForDegreePrograms, $allowedDegreePrograms);
|
||||||
|
|
||||||
// getting the number of rows of the query and adding pagination to the query result
|
// getting the number of rows of the query and adding pagination to the query result
|
||||||
$num_rows = $this->getNumRows(true);
|
$num_rows = $this->getNumRows(true);
|
||||||
$this->addPagination($page, $page_size, $num_rows);
|
$this->addPagination($page, $page_size, $num_rows);
|
||||||
|
|
||||||
// preparing the query again because every call to get_compiled_select or cour_all_results will add the from clause to the query
|
// preparing the query again because every call to get_compiled_select or cour_all_results will add the from clause to the query
|
||||||
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen);
|
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $page, $page_size, $active, $mischen, $filterForDegreePrograms, $allowedDegreePrograms);
|
||||||
|
|
||||||
return $this->load();
|
return $this->load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function countNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz = null, $sichtbar = true, $maxalter = 0, $page = 1, $page_size = 10, $all = false, $mischen = true)
|
|
||||||
{
|
|
||||||
$this->prepareNewsWithContent($sprache, $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen);
|
|
||||||
return $this->getNumRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,30 @@ class Prestudent_model extends DB_Model
|
|||||||
return $this->execQuery($query, $parametersArray);
|
return $this->execQuery($query, $parametersArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Studiengang and current Ausbildungssemester for every
|
||||||
|
* Prestudent belonging to the same person as the student.
|
||||||
|
*
|
||||||
|
* @param string $student_uid
|
||||||
|
* @return stdClass
|
||||||
|
*/
|
||||||
|
public function getStudiengaengAndAusbildungssemesterByStudentUid($student_uid)
|
||||||
|
{
|
||||||
|
$query = 'SELECT p.studiengang_kz,
|
||||||
|
(
|
||||||
|
SELECT ps.ausbildungssemester
|
||||||
|
FROM public.tbl_prestudentstatus ps
|
||||||
|
WHERE ps.prestudent_id = p.prestudent_id
|
||||||
|
ORDER BY ps.datum DESC, ps.insertamum DESC, ps.ext_id DESC
|
||||||
|
LIMIT 1
|
||||||
|
) AS ausbildungssemester
|
||||||
|
FROM public.tbl_student s
|
||||||
|
JOIN public.tbl_prestudent p USING (prestudent_id)
|
||||||
|
WHERE s.student_uid = ?';
|
||||||
|
|
||||||
|
return $this->execQuery($query, array($student_uid));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updateAufnahmegruppe
|
* updateAufnahmegruppe
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -885,6 +885,14 @@ class Studiengang_model extends DB_Model
|
|||||||
return success($ass);
|
return success($ass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDegreePrograms() {
|
||||||
|
$query = "SELECT *
|
||||||
|
FROM public.tbl_studiengang
|
||||||
|
ORDER BY public.tbl_studiengang.kurzbzlang";
|
||||||
|
|
||||||
|
return $this->execReadOnlyQuery($query);
|
||||||
|
}
|
||||||
|
|
||||||
private function addEmailProperty(&$benutzerfunktionen) {
|
private function addEmailProperty(&$benutzerfunktionen) {
|
||||||
if(count($benutzerfunktionen) && defined('DOMAIN'))
|
if(count($benutzerfunktionen) && defined('DOMAIN'))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ $includesArray = array(
|
|||||||
'primevue3' => true,
|
'primevue3' => true,
|
||||||
'skipID' => '#fhccontent',
|
'skipID' => '#fhccontent',
|
||||||
'vuedatepicker11' => true,
|
'vuedatepicker11' => true,
|
||||||
|
'tinymce5' => true,
|
||||||
'customCSSs' => array(
|
'customCSSs' => array(
|
||||||
'public/css/components/verticalsplit.css',
|
'public/css/components/verticalsplit.css',
|
||||||
'public/css/components/searchbar/searchbar.css',
|
'public/css/components/searchbar/searchbar.css',
|
||||||
@@ -23,7 +24,8 @@ $includesArray = array(
|
|||||||
'public/css/components/FormUnderline.css',
|
'public/css/components/FormUnderline.css',
|
||||||
'public/css/components/abgabetool/abgabe.css',
|
'public/css/components/abgabetool/abgabe.css',
|
||||||
'public/css/Cis4/Cms.css',
|
'public/css/Cis4/Cms.css',
|
||||||
'public/css/Cis4/Studium.css'
|
'public/css/Cis4/Studium.css',
|
||||||
|
'public/css/components/vue-datepicker.css',
|
||||||
),
|
),
|
||||||
'customJSs' => array(
|
'customJSs' => array(
|
||||||
'vendor/npm-asset/primevue/accordion/accordion.min.js',
|
'vendor/npm-asset/primevue/accordion/accordion.min.js',
|
||||||
@@ -49,6 +51,8 @@ $includesArray = array(
|
|||||||
$this->load->view('templates/CISVUE-Header', $includesArray);
|
$this->load->view('templates/CISVUE-Header', $includesArray);
|
||||||
?>
|
?>
|
||||||
<div id="fhccontent" class="h-100" route=<?php echo $route ?>>
|
<div id="fhccontent" class="h-100" route=<?php echo $route ?>>
|
||||||
<router-view></router-view>
|
<router-view
|
||||||
|
:permissions="<?= htmlspecialchars(json_encode($permissions ?? [])); ?>"
|
||||||
|
></router-view>
|
||||||
</div>
|
</div>
|
||||||
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
|
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
|
||||||
|
|||||||
@@ -86,11 +86,11 @@ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
|||||||
<link rel="stylesheet" href="../skin/styles/jquery.css" type="text/css">
|
<link rel="stylesheet" href="../skin/styles/jquery.css" type="text/css">
|
||||||
<link rel="stylesheet" href="../vendor/components/jqueryui/themes/base/jquery-ui.min.css" type="text/css">
|
<link rel="stylesheet" href="../vendor/components/jqueryui/themes/base/jquery-ui.min.css" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="../skin/jquery-ui-1.9.2.custom.min.css">
|
<link rel="stylesheet" type="text/css" href="../skin/jquery-ui-1.9.2.custom.min.css">
|
||||||
<script type="text/javascript" src="../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
|
<script type="text/javascript" src="../vendor/jquery/jquery1/jquery-1.12.4.min.js"></script>
|
||||||
<script type="text/javascript" src="../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
|
<script type="text/javascript" src="../vendor/christianbach/tablesorter/jquery.tablesorter.min.js"></script>
|
||||||
<script type="text/javascript" src="../vendor/components/jqueryui/jquery-ui.min.js"></script>
|
<script type="text/javascript" src="../vendor/components/jqueryui/jquery-ui.min.js"></script>
|
||||||
<script type="text/javascript" src="../include/js/jquery.ui.datepicker.translation.js"></script>
|
<script type="text/javascript" src="../include/js/jquery.ui.datepicker.translation.js"></script>
|
||||||
<script type="text/javascript" src="../vendor/jquery/sizzle/sizzle.js"></script>
|
<script type="text/javascript" src="../vendor/jquery/sizzle/sizzle.js"></script>
|
||||||
<script src="../vendor/components/jqueryui/jquery-ui.min.js" type="text/javascript"></script>
|
<script src="../vendor/components/jqueryui/jquery-ui.min.js" type="text/javascript"></script>
|
||||||
<script type="text/javascript" src="../include/tiny_mce/tiny_mce.js"></script>
|
<script type="text/javascript" src="../include/tiny_mce/tiny_mce.js"></script>
|
||||||
|
|||||||
@@ -88,6 +88,29 @@ if (! $rechte->isberechtigt('basis/dms', null, 's', null))
|
|||||||
init: function(){
|
init: function(){
|
||||||
},
|
},
|
||||||
mySubmit : function (id) {
|
mySubmit : function (id) {
|
||||||
|
var messageTarget = window.opener;
|
||||||
|
|
||||||
|
if (!messageTarget && window.parent !== window)
|
||||||
|
messageTarget = window.parent;
|
||||||
|
|
||||||
|
if (messageTarget && typeof(messageTarget.postMessage) == "function")
|
||||||
|
{
|
||||||
|
messageTarget.postMessage(
|
||||||
|
{
|
||||||
|
type: "fhcomplete:dms:selected",
|
||||||
|
dmsId: id
|
||||||
|
},
|
||||||
|
window.location.protocol + "//" + window.location.host
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof(tinyMCEPopup) == "undefined" ||
|
||||||
|
!tinyMCEPopup.params ||
|
||||||
|
typeof(tinyMCEPopup.getWindowArg) != "function"
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
var URL = "dms.php?id="+id;
|
var URL = "dms.php?id="+id;
|
||||||
var win = tinyMCEPopup.getWindowArg("window");
|
var win = tinyMCEPopup.getWindowArg("window");
|
||||||
|
|
||||||
|
|||||||
+17
-38
@@ -1,45 +1,24 @@
|
|||||||
export default {
|
export default {
|
||||||
content(content_id, version=null, sprache=null, sichtbar=null) {
|
content(content_id, version = null, sprache = null, sichtbar = null) {
|
||||||
return this.$fhcApi.get(
|
return this.$fhcApi.get('/api/frontend/v1/Cms/content', {
|
||||||
"/api/frontend/v1/Cms/content",
|
content_id: content_id,
|
||||||
{
|
...(version ? { version } : {}),
|
||||||
content_id: content_id,
|
...(sprache ? { sprache } : {}),
|
||||||
...(version?{version}:{}),
|
...(sichtbar ? { sichtbar } : {}),
|
||||||
...(sprache?{sprache}:{}),
|
});
|
||||||
...(sichtbar?{sichtbar}:{}),
|
},
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
//api function used for the news View that renders the html
|
//api function used for the news View that renders the html
|
||||||
getNews(page = 1, page_size = 10, sprache) {
|
getNews(page = 1, page_size = 10, sprache) {
|
||||||
return this.$fhcApi.get(
|
return this.$fhcApi.get('/api/frontend/v1/Cms/getNews', {
|
||||||
"/api/frontend/v1/Cms/getNews",
|
page,
|
||||||
{
|
page_size,
|
||||||
page,
|
sprache,
|
||||||
page_size,
|
});
|
||||||
sprache,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
//api function used for the widget component
|
//api function used for the widget component
|
||||||
news(limit) {
|
news(limit) {
|
||||||
return this.$fhcApi.get(
|
return this.$fhcApi.get('/api/frontend/v1/Cms/news', {
|
||||||
"/api/frontend/v1/Cms/news",
|
limit: limit,
|
||||||
{
|
});
|
||||||
limit: limit
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
getNewsRowCount: function () {
|
};
|
||||||
return this.$fhcApi.get(
|
|
||||||
"/api/frontend/v1/Cms/getNewsRowCount",
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
getNewsExtra: function(){
|
|
||||||
return this.$fhcApi.get(
|
|
||||||
"/api/frontend/v1/Cms/getStudiengangInfoForNews",
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content(content_id, version=null, sprache=null, sichtbar=null) {
|
content(content_id, version = null, sprache = null, sichtbar = null) {
|
||||||
return {
|
return {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/api/frontend/v1/Cms/content',
|
url: '/api/frontend/v1/Cms/content',
|
||||||
@@ -24,41 +24,29 @@ export default {
|
|||||||
content_id,
|
content_id,
|
||||||
...(version ? { version } : {}),
|
...(version ? { version } : {}),
|
||||||
...(sprache ? { sprache } : {}),
|
...(sprache ? { sprache } : {}),
|
||||||
...(sichtbar ? { sichtbar } : {})
|
...(sichtbar ? { sichtbar } : {}),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
//api function used for the news View that renders the html
|
//api function used for the news View that renders the html
|
||||||
getNews(page = 1, page_size = 10, sprache) {
|
getNews(page = 1, page_size = 10, sprache, filterPublished = true) {
|
||||||
return {
|
return {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/api/frontend/v1/Cms/getNews',
|
url: '/api/frontend/v1/Cms/getNews',
|
||||||
params: {
|
params: {
|
||||||
page,
|
page,
|
||||||
page_size,
|
page_size,
|
||||||
sprache
|
sprache,
|
||||||
|
published: filterPublished,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
//api function used for the widget component
|
//api function used for the widget component
|
||||||
news(limit) {
|
newsForWidget(limit) {
|
||||||
return {
|
return {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/api/frontend/v1/Cms/news',
|
url: '/api/frontend/v1/Cms/newsForWidget',
|
||||||
params: { limit }
|
params: { limit },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getNewsRowCount() {
|
};
|
||||||
return {
|
|
||||||
method: 'get',
|
|
||||||
url: '/api/frontend/v1/Cms/getNewsRowCount'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getNewsExtra() {
|
|
||||||
// TODO(chris): seems to be called from nowhere?
|
|
||||||
return {
|
|
||||||
method: 'get',
|
|
||||||
url: '/api/frontend/v1/Cms/getStudiengangInfoForNews'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2025 fhcomplete.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getNews(
|
||||||
|
page = 1,
|
||||||
|
page_size = 10,
|
||||||
|
sprache,
|
||||||
|
publishedFilter = true,
|
||||||
|
isActive = true,
|
||||||
|
degreeProgramShortCode = null,
|
||||||
|
semester = null,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/NewsAdministrationAPI/getNews',
|
||||||
|
params: {
|
||||||
|
page,
|
||||||
|
page_size,
|
||||||
|
sprache,
|
||||||
|
published: publishedFilter,
|
||||||
|
isActive,
|
||||||
|
degreeProgramShortCode,
|
||||||
|
semester,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getNewsItem(newsId) {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/frontend/v1/NewsAdministrationAPI/getNewsItem/${newsId}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
storeNewsItem(data) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/frontend/v1/NewsAdministrationAPI/storeNewsItem',
|
||||||
|
params: data,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
updateNewsItem(newsId, data) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: `/api/frontend/v1/NewsAdministrationAPI/updateNewsItem/${newsId}`,
|
||||||
|
params: data,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
deleteNewsItem(newsId) {
|
||||||
|
return {
|
||||||
|
method: 'post',
|
||||||
|
url: `/api/frontend/v1/NewsAdministrationAPI/deleteNewsItem/${newsId}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -16,17 +16,23 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
getDegreePrograms() {
|
||||||
|
return {
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/frontend/v1/Studgang/getDegreePrograms',
|
||||||
|
};
|
||||||
|
},
|
||||||
studiengangInformation() {
|
studiengangInformation() {
|
||||||
return {
|
return {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/api/frontend/v1/Studgang/getStudiengangInfo'
|
url: '/api/frontend/v1/Studgang/getStudiengangInfo',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getStudiengangByKz(studiengang_kz) {
|
getStudiengangByKz(studiengang_kz) {
|
||||||
return {
|
return {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/api/frontend/v1/organisation/StudiengangEP/getStudiengangByKz',
|
url: '/api/frontend/v1/organisation/StudiengangEP/getStudiengangByKz',
|
||||||
params: { studiengang_kz }
|
params: { studiengang_kz },
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+135
-106
@@ -2,29 +2,35 @@ import FhcDashboard from '../../components/Dashboard/Dashboard.js';
|
|||||||
import PluginsPhrasen from '../../plugins/Phrasen.js';
|
import PluginsPhrasen from '../../plugins/Phrasen.js';
|
||||||
import Theme from '../../plugins/Theme.js';
|
import Theme from '../../plugins/Theme.js';
|
||||||
import contrast from '../../directives/contrast.js';
|
import contrast from '../../directives/contrast.js';
|
||||||
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers.js";
|
import { setScrollbarWidth } from '../../helpers/CssVarCalcHelpers.js';
|
||||||
import LvPlan from "../../components/Cis/LvPlan/Lehrveranstaltung.js";
|
import LvPlan from '../../components/Cis/LvPlan/Lehrveranstaltung.js';
|
||||||
import MyLvPlan from "../../components/Cis/LvPlan/MyLvPlan.js";
|
import MyLvPlan from '../../components/Cis/LvPlan/MyLvPlan.js';
|
||||||
import MylvStudent from "../../components/Cis/Mylv/Student.js";
|
import MylvStudent from '../../components/Cis/Mylv/Student.js';
|
||||||
import Profil from "../../components/Cis/Profil/Profil.js";
|
import Profil from '../../components/Cis/Profil/Profil.js';
|
||||||
import Raumsuche from "../../components/Cis/Raumsuche/Raumsuche.js";
|
import Raumsuche from '../../components/Cis/Raumsuche/Raumsuche.js';
|
||||||
import CmsNews from "../../components/Cis/Cms/News.js";
|
import CmsNews from '../../components/Cis/Cms/News.js';
|
||||||
import CmsContent from "../../components/Cis/Cms/Content.js";
|
import NewsAdministration from '../../components/Cis/Cms/NewsAdministration/NewsAdministration.js';
|
||||||
import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
|
import CmsContent from '../../components/Cis/Cms/Content.js';
|
||||||
import RoomInformation, {DEFAULT_MODE_RAUMINFO_DESKTOP, DEFAULT_MODE_RAUMINFO_MOBILE} from "../../components/Cis/Mylv/RoomInformation.js";
|
import Info from '../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js';
|
||||||
import AbgabetoolStudent from "../../components/Cis/Abgabetool/AbgabetoolStudent.js";
|
import RoomInformation, {
|
||||||
import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js";
|
DEFAULT_MODE_RAUMINFO_DESKTOP,
|
||||||
import AbgabetoolAssistenz from "../../components/Cis/Abgabetool/AbgabetoolAssistenz.js";
|
DEFAULT_MODE_RAUMINFO_MOBILE,
|
||||||
import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.js";
|
} from '../../components/Cis/Mylv/RoomInformation.js';
|
||||||
import Studium from "../../components/Cis/Studium/Studium.js";
|
import AbgabetoolStudent from '../../components/Cis/Abgabetool/AbgabetoolStudent.js';
|
||||||
import StgOrgLvPlan from "../../components/Cis/LvPlan/StgOrg.js";
|
import AbgabetoolMitarbeiter from '../../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js';
|
||||||
import OtherLvPlan from "../../components/Cis/LvPlan/OtherLvPlan.js";
|
import AbgabetoolAssistenz from '../../components/Cis/Abgabetool/AbgabetoolAssistenz.js';
|
||||||
|
import DeadlineOverview from '../../components/Cis/Abgabetool/DeadlineOverview.js';
|
||||||
|
import Studium from '../../components/Cis/Studium/Studium.js';
|
||||||
|
import StgOrgLvPlan from '../../components/Cis/LvPlan/StgOrg.js';
|
||||||
|
import OtherLvPlan from '../../components/Cis/LvPlan/OtherLvPlan.js';
|
||||||
|
|
||||||
import ApiRouteInfo from '../../api/factory/routeinfo.js';
|
import ApiRouteInfo from '../../api/factory/routeinfo.js';
|
||||||
import {capitalize} from "../../helpers/StringHelpers.js";
|
import { capitalize } from '../../helpers/StringHelpers.js';
|
||||||
|
|
||||||
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
const ciPath =
|
||||||
const isMobile = window.matchMedia("(max-width: 767px)").matches;
|
FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') +
|
||||||
|
FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
||||||
|
const isMobile = window.matchMedia('(max-width: 767px)').matches;
|
||||||
|
|
||||||
const router = VueRouter.createRouter({
|
const router = VueRouter.createRouter({
|
||||||
history: VueRouter.createWebHistory(`/${ciPath}`),
|
history: VueRouter.createWebHistory(`/${ciPath}`),
|
||||||
@@ -33,62 +39,66 @@ const router = VueRouter.createRouter({
|
|||||||
path: `/Cis/Studium`,
|
path: `/Cis/Studium`,
|
||||||
name: 'Studium',
|
name: 'Studium',
|
||||||
component: Studium,
|
component: Studium,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/Profil/View/:uid`,
|
path: `/Cis/Profil/View/:uid`,
|
||||||
name: 'ProfilView',
|
name: 'ProfilView',
|
||||||
component: Profil,
|
component: Profil,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/Profil`,
|
path: `/Cis/Profil`,
|
||||||
name: 'Profil',
|
name: 'Profil',
|
||||||
component: Profil,
|
component: Profil,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/Abgabetool/Student/:student_uid_prop?`,
|
path: `/Cis/Abgabetool/Student/:student_uid_prop?`,
|
||||||
name: 'AbgabetoolStudent',
|
name: 'AbgabetoolStudent',
|
||||||
component: AbgabetoolStudent,
|
component: AbgabetoolStudent,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/Abgabetool/Mitarbeiter`,
|
path: `/Cis/Abgabetool/Mitarbeiter`,
|
||||||
name: 'AbgabetoolMitarbeiter',
|
name: 'AbgabetoolMitarbeiter',
|
||||||
component: AbgabetoolMitarbeiter,
|
component: AbgabetoolMitarbeiter,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/Abgabetool/Assistenz/:stg_kz_prop?`,
|
path: `/Cis/Abgabetool/Assistenz/:stg_kz_prop?`,
|
||||||
name: 'AbgabetoolAssistenz',
|
name: 'AbgabetoolAssistenz',
|
||||||
component: AbgabetoolAssistenz,
|
component: AbgabetoolAssistenz,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/Abgabetool/Deadlines/:person_uid_prop?`,
|
path: `/Cis/Abgabetool/Deadlines/:person_uid_prop?`,
|
||||||
name: 'DeadlineOverview',
|
name: 'DeadlineOverview',
|
||||||
component: DeadlineOverview,
|
component: DeadlineOverview,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/Raumsuche`,
|
path: `/Cis/Raumsuche`,
|
||||||
name: 'Raumsuche',
|
name: 'Raumsuche',
|
||||||
component: Raumsuche,
|
component: Raumsuche,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
// Redirect old links to new format
|
// Redirect old links to new format
|
||||||
{
|
{
|
||||||
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
|
path: '/CisVue/Cms/getRoomInformation/:ort_kurzbz',
|
||||||
name: "RoomInformationOld",
|
name: 'RoomInformationOld',
|
||||||
component: RoomInformation,
|
component: RoomInformation,
|
||||||
redirect: (to) => {
|
redirect: (to) => {
|
||||||
return { // redirect to longer Rauminfo url and map params
|
return {
|
||||||
name: "RoomInformation",
|
// redirect to longer Rauminfo url and map params
|
||||||
params: { // in this case always populate other params since they are not optional
|
name: 'RoomInformation',
|
||||||
|
params: {
|
||||||
|
// in this case always populate other params since they are not optional
|
||||||
ort_kurzbz: to.params.ort_kurzbz,
|
ort_kurzbz: to.params.ort_kurzbz,
|
||||||
mode: isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP,
|
mode: isMobile
|
||||||
focus_date: new Date().toISOString().split("T")[0]
|
? DEFAULT_MODE_RAUMINFO_MOBILE
|
||||||
|
: DEFAULT_MODE_RAUMINFO_DESKTOP,
|
||||||
|
focus_date: new Date().toISOString().split('T')[0],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -97,18 +107,28 @@ const router = VueRouter.createRouter({
|
|||||||
path: `/CisVue/Cms/getRoomInformation/:mode/:focus_date/:ort_kurzbz`,
|
path: `/CisVue/Cms/getRoomInformation/:mode/:focus_date/:ort_kurzbz`,
|
||||||
name: 'RoomInformation',
|
name: 'RoomInformation',
|
||||||
component: RoomInformation,
|
component: RoomInformation,
|
||||||
props: (route) => { // validate and set mode/focus date if for some reason missing
|
props: (route) => {
|
||||||
const validModes = ["Month", "Week", "Day"];
|
// validate and set mode/focus date if for some reason missing
|
||||||
|
const validModes = ['Month', 'Week', 'Day'];
|
||||||
|
|
||||||
// check mode string
|
// check mode string
|
||||||
const mode = route.params.mode &&
|
const mode =
|
||||||
validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase())
|
route.params.mode &&
|
||||||
? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()
|
validModes.includes(
|
||||||
: (isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP);
|
route.params.mode.charAt(0).toUpperCase() +
|
||||||
|
route.params.mode.slice(1).toLowerCase(),
|
||||||
|
)
|
||||||
|
? route.params.mode.charAt(0).toUpperCase() +
|
||||||
|
route.params.mode.slice(1).toLowerCase()
|
||||||
|
: isMobile
|
||||||
|
? DEFAULT_MODE_RAUMINFO_MOBILE
|
||||||
|
: DEFAULT_MODE_RAUMINFO_DESKTOP;
|
||||||
|
|
||||||
// default to today date if not provided
|
// default to today date if not provided
|
||||||
const d = new Date(route.params.focus_date)
|
const d = new Date(route.params.focus_date);
|
||||||
const focus_date = !isNaN(d) ? route.params.focus_date : new Date().toISOString().split("T")[0];
|
const focus_date = !isNaN(d)
|
||||||
|
? route.params.focus_date
|
||||||
|
: new Date().toISOString().split('T')[0];
|
||||||
|
|
||||||
// for consistency reasons format the props into one object but actually use a new name to we dont collide with
|
// for consistency reasons format the props into one object but actually use a new name to we dont collide with
|
||||||
// existing viewData declaration written from codeigniter 3 into routerview tag
|
// existing viewData declaration written from codeigniter 3 into routerview tag
|
||||||
@@ -116,37 +136,44 @@ const router = VueRouter.createRouter({
|
|||||||
propsViewData: {
|
propsViewData: {
|
||||||
mode,
|
mode,
|
||||||
focus_date,
|
focus_date,
|
||||||
ort_kurzbz: route.params.ort_kurzbz
|
ort_kurzbz: route.params.ort_kurzbz,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
beforeEnter: (to, from, next) => {
|
beforeEnter: (to, from, next) => {
|
||||||
// missing mode or focus_date -> set defaults
|
// missing mode or focus_date -> set defaults
|
||||||
if (!to.params.mode || !to.params.focus_date) {
|
if (!to.params.mode || !to.params.focus_date) {
|
||||||
next({
|
next({
|
||||||
name: "RoomInformation",
|
name: 'RoomInformation',
|
||||||
params: {
|
params: {
|
||||||
mode: to.params.mode || DEFAULT_MODE_RAUMINFO,
|
mode: to.params.mode || DEFAULT_MODE_RAUMINFO,
|
||||||
focus_date: to.params.focus_date || new Date().toISOString().split("T")[0],
|
focus_date:
|
||||||
ort_kurzbz: route.params.ort_kurzbz
|
to.params.focus_date || new Date().toISOString().split('T')[0],
|
||||||
}
|
ort_kurzbz: route.params.ort_kurzbz,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/CisVue/Cms/Content/:content_id`,
|
path: `/CisVue/Cms/Content/:content_id`,
|
||||||
name: 'Content',
|
name: 'Content',
|
||||||
component: CmsContent,
|
component: CmsContent,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/CisVue/Cms/News`,
|
path: `/CisVue/Cms/News`,
|
||||||
name: 'News',
|
name: 'News',
|
||||||
component: CmsNews,
|
component: CmsNews,
|
||||||
props: true
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/CisVue/Cms/newsAdministration`,
|
||||||
|
name: 'NewsAdministration',
|
||||||
|
component: NewsAdministration,
|
||||||
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/MyLv/:studiensemester?`,
|
path: `/Cis/MyLv/:studiensemester?`,
|
||||||
@@ -158,23 +185,24 @@ const router = VueRouter.createRouter({
|
|||||||
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
|
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
|
||||||
name: 'LvInfo',
|
name: 'LvInfo',
|
||||||
component: Info,
|
component: Info,
|
||||||
props: true
|
props: true,
|
||||||
},
|
},
|
||||||
// Redirect old links to new format
|
// Redirect old links to new format
|
||||||
{
|
{
|
||||||
// only trigger on first param being numeric to avoid paths like "LvPlan/Month" entering here
|
// only trigger on first param being numeric to avoid paths like "LvPlan/Month" entering here
|
||||||
path: "/Cis/LvPlan/:lv_id(\\d+)",
|
path: '/Cis/LvPlan/:lv_id(\\d+)',
|
||||||
name: "LvPlanOld",
|
name: 'LvPlanOld',
|
||||||
component: LvPlan,
|
component: LvPlan,
|
||||||
redirect(to) {
|
redirect(to) {
|
||||||
const route = Vue.unref(router.currentRoute);
|
const route = Vue.unref(router.currentRoute);
|
||||||
const { mode, focus_date } = route.params; // keep mode and focus_date if available
|
const { mode, focus_date } = route.params; // keep mode and focus_date if available
|
||||||
return { // redirect to longer LvPlan url and map params
|
return {
|
||||||
name: "LvPlan",
|
// redirect to longer LvPlan url and map params
|
||||||
|
name: 'LvPlan',
|
||||||
params: {
|
params: {
|
||||||
mode,
|
mode,
|
||||||
focus_date,
|
focus_date,
|
||||||
lv_id: to.params.lv_id
|
lv_id: to.params.lv_id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -185,9 +213,9 @@ const router = VueRouter.createRouter({
|
|||||||
component: LvPlan,
|
component: LvPlan,
|
||||||
props(route) {
|
props(route) {
|
||||||
return {
|
return {
|
||||||
propsViewData: route.params
|
propsViewData: route.params,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/MyLvPlan/:mode?/:focus_date?`,
|
path: `/Cis/MyLvPlan/:mode?/:focus_date?`,
|
||||||
@@ -195,9 +223,9 @@ const router = VueRouter.createRouter({
|
|||||||
component: MyLvPlan,
|
component: MyLvPlan,
|
||||||
props(route) {
|
props(route) {
|
||||||
return {
|
return {
|
||||||
propsViewData: route.params
|
propsViewData: route.params,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/StgOrgLvPlan/:mode?/:focus_date?/:stgkz?/:sem?/:verband?/:gruppe?`,
|
path: `/Cis/StgOrgLvPlan/:mode?/:focus_date?/:stgkz?/:sem?/:verband?/:gruppe?`,
|
||||||
@@ -205,48 +233,48 @@ const router = VueRouter.createRouter({
|
|||||||
component: StgOrgLvPlan,
|
component: StgOrgLvPlan,
|
||||||
props(route) {
|
props(route) {
|
||||||
return {
|
return {
|
||||||
propsViewData: route.params
|
propsViewData: route.params,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/OtherLvPlan/:otherUid/:mode?/:focus_date?`,
|
path: `/Cis/OtherLvPlan/:otherUid/:mode?/:focus_date?`,
|
||||||
name: "OtherLvPlan",
|
name: 'OtherLvPlan',
|
||||||
component: OtherLvPlan,
|
component: OtherLvPlan,
|
||||||
props(route) {
|
props(route) {
|
||||||
return {
|
return {
|
||||||
propsViewData: route.params
|
propsViewData: route.params,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis4`,
|
path: `/Cis4`,
|
||||||
name: 'Cis4',
|
name: 'Cis4',
|
||||||
component: FhcDashboard,
|
component: FhcDashboard,
|
||||||
props: {dashboard: 'CIS'},
|
props: { dashboard: 'CIS' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/`,
|
path: `/`,
|
||||||
name: 'FhcDashboard',
|
name: 'FhcDashboard',
|
||||||
component: FhcDashboard,
|
component: FhcDashboard,
|
||||||
props: {dashboard: 'CIS'},
|
props: { dashboard: 'CIS' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
name: 'Fallback',
|
name: 'Fallback',
|
||||||
component: FhcDashboard,
|
component: FhcDashboard,
|
||||||
props: {dashboard: 'CIS'},
|
props: { dashboard: 'CIS' },
|
||||||
redirect: () => {
|
redirect: () => {
|
||||||
return {
|
return {
|
||||||
name: "Cis4",
|
name: 'Cis4',
|
||||||
params: {
|
params: {
|
||||||
dashboard: 'CIS'
|
dashboard: 'CIS',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
const app = Vue.createApp({
|
const app = Vue.createApp({
|
||||||
name: 'CisApp',
|
name: 'CisApp',
|
||||||
@@ -255,48 +283,49 @@ const app = Vue.createApp({
|
|||||||
windowWidth: 0,
|
windowWidth: 0,
|
||||||
}),
|
}),
|
||||||
provide() {
|
provide() {
|
||||||
return { // provide injectable & watchable language property
|
return {
|
||||||
|
// provide injectable & watchable language property
|
||||||
language: Vue.computed(() => this.$p.user_language),
|
language: Vue.computed(() => this.$p.user_language),
|
||||||
isMobile: Vue.computed(() => this.isMobile),
|
isMobile: Vue.computed(() => this.isMobile),
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isMobile: function() {
|
isMobile: function () {
|
||||||
return (this.windowWidth < 767);
|
return this.windowWidth < 767;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isInternalRoute(href) {
|
isInternalRoute(href) {
|
||||||
const internalBase = window.location.origin
|
const internalBase = window.location.origin;
|
||||||
return href.startsWith(internalBase);
|
return href.startsWith(internalBase);
|
||||||
},
|
},
|
||||||
handleClick(event) {
|
handleClick(event) {
|
||||||
const target = event.target.closest('a');
|
const target = event.target.closest('a');
|
||||||
|
|
||||||
if(target?.id == 'skiplink') return
|
if (target?.id == 'skiplink') return;
|
||||||
if (target && this.isInternalRoute(target.href)) {
|
if (target && this.isInternalRoute(target.href)) {
|
||||||
const url = new URL(target.href)
|
const url = new URL(target.href);
|
||||||
|
|
||||||
const path = url.pathname
|
const path = url.pathname;
|
||||||
const base = this.$router.options.history.base
|
const base = this.$router.options.history.base;
|
||||||
const route = path.replace(base, '') || '/'
|
const route = path.replace(base, '') || '/';
|
||||||
|
|
||||||
// let click event propagate normally if we dont route internally
|
// let click event propagate normally if we dont route internally
|
||||||
const res = this.$router.resolve(route)
|
const res = this.$router.resolve(route);
|
||||||
if(!res?.matched?.length || res.name === 'Fallback') return
|
if (!res?.matched?.length || res.name === 'Fallback') return;
|
||||||
|
|
||||||
event.preventDefault(); // Prevent browser navigation
|
event.preventDefault(); // Prevent browser navigation
|
||||||
|
|
||||||
if(this.isMobile) { // toggle the menu
|
if (this.isMobile) {
|
||||||
|
// toggle the menu
|
||||||
const navMain = document.getElementById('nav-main');
|
const navMain = document.getElementById('nav-main');
|
||||||
// fix unwanted toggle from off to on for some links on mobile
|
// fix unwanted toggle from off to on for some links on mobile
|
||||||
if(navMain.classList.contains('show')){
|
if (navMain.classList.contains('show')) {
|
||||||
document.getElementById('nav-main-btn').click();
|
document.getElementById('nav-main-btn').click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$router.push(route);
|
this.$router.push(route);
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleWindowResize() {
|
handleWindowResize() {
|
||||||
@@ -308,11 +337,11 @@ const app = Vue.createApp({
|
|||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
document.addEventListener('click', this.handleClick);
|
document.addEventListener('click', this.handleClick);
|
||||||
window.addEventListener("resize", this.handleWindowResize);
|
window.addEventListener('resize', this.handleWindowResize);
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
document.removeEventListener('click', this.handleClick);
|
document.removeEventListener('click', this.handleClick);
|
||||||
window.removeEventListener("resize", this.handleWindowResize);
|
window.removeEventListener('resize', this.handleWindowResize);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -327,9 +356,9 @@ app.use(router);
|
|||||||
app.use(primevue.config.default, {
|
app.use(primevue.config.default, {
|
||||||
zIndex: {
|
zIndex: {
|
||||||
overlay: 9000,
|
overlay: 9000,
|
||||||
tooltip: 8000
|
tooltip: 8000,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
app.directive('tooltip', primevue.tooltip);
|
app.directive('tooltip', primevue.tooltip);
|
||||||
app.use(PluginsPhrasen);
|
app.use(PluginsPhrasen);
|
||||||
app.use(Theme);
|
app.use(Theme);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import FhcCalendar from "./Base.js";
|
import FhcCalendar from './Base.js';
|
||||||
|
|
||||||
import ApiLvPlan from '../../api/factory/lvPlan.js';
|
import ApiLvPlan from '../../api/factory/lvPlan.js';
|
||||||
|
|
||||||
@@ -11,50 +11,48 @@ import ModeMonth from './Mode/Month.js';
|
|||||||
import ModeList from './Mode/List.js';
|
import ModeList from './Mode/List.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CalendarLvPlan",
|
name: 'CalendarLvPlan',
|
||||||
components: {
|
components: {
|
||||||
FhcCalendar
|
FhcCalendar,
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
isMobile: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
inject: ["isMobile"],
|
|
||||||
props: {
|
props: {
|
||||||
date: {
|
date: {
|
||||||
type: [Date, String, Number, luxon.DateTime],
|
type: [Date, String, Number, luxon.DateTime],
|
||||||
default: luxon.DateTime.local()
|
default: luxon.DateTime.local(),
|
||||||
},
|
},
|
||||||
mode: {
|
mode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'Week'
|
default: 'Week',
|
||||||
},
|
},
|
||||||
getPromiseFunc: {
|
getPromiseFunc: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
shouldCompactEvents: Vue.computed(
|
shouldCompactEvents: Vue.computed(
|
||||||
() => this.$props.mode === "Month" && this.isMobile,
|
() => this.$props.mode === 'Month' && this.isMobile,
|
||||||
),
|
|
||||||
compactibleEventTypes: Vue.computed(
|
|
||||||
() => this.compactibleEventTypes,
|
|
||||||
),
|
),
|
||||||
|
compactibleEventTypes: Vue.computed(() => this.compactibleEventTypes),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: [
|
emits: ['update:date', 'update:mode', 'update:range'],
|
||||||
"update:date",
|
|
||||||
"update:mode",
|
|
||||||
"update:range"
|
|
||||||
],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||||
modeOptions: {
|
modeOptions: {
|
||||||
day: {
|
day: {
|
||||||
emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')),
|
emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')),
|
||||||
emptyMessageDetails: Vue.computed(() => this.$p.t('lehre/noLvFound'))
|
emptyMessageDetails: Vue.computed(() => this.$p.t('lehre/noLvFound')),
|
||||||
},
|
},
|
||||||
week: {
|
week: {
|
||||||
collapseEmptyDays: false
|
collapseEmptyDays: false,
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
length: 7,
|
length: 7,
|
||||||
@@ -72,16 +70,18 @@ export default {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
class: 'background-past',
|
class: 'background-past',
|
||||||
end: now.startOf('day')
|
end: now.startOf('day'),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
class: 'background-past',
|
class: 'background-past',
|
||||||
end: now,
|
end: now,
|
||||||
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
|
label: now
|
||||||
}
|
.startOf('minute')
|
||||||
|
.toISOTime({ suppressSeconds: true, includeOffset: false }),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
modes() {
|
modes() {
|
||||||
@@ -100,8 +100,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
eventStyle(event) {
|
eventStyle(event) {
|
||||||
if (!event.farbe)
|
if (!event.farbe) return undefined;
|
||||||
return undefined;
|
|
||||||
return '--event-bg:#' + event.farbe;
|
return '--event-bg:#' + event.farbe;
|
||||||
},
|
},
|
||||||
updateRange(rangeInterval) {
|
updateRange(rangeInterval) {
|
||||||
@@ -128,10 +127,13 @@ export default {
|
|||||||
},
|
},
|
||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
const rangeInterval = Vue.ref(null);
|
const rangeInterval = Vue.ref(null);
|
||||||
|
|
||||||
const { events, lv, reset } = useEventLoader(rangeInterval, props.getPromiseFunc);
|
|
||||||
|
|
||||||
Vue.watch(lv, newValue => {
|
const { events, lv, reset } = useEventLoader(
|
||||||
|
rangeInterval,
|
||||||
|
props.getPromiseFunc,
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.watch(lv, (newValue) => {
|
||||||
context.emit('update:lv', newValue);
|
context.emit('update:lv', newValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -142,14 +144,14 @@ export default {
|
|||||||
events,
|
events,
|
||||||
lv,
|
lv,
|
||||||
reset,
|
reset,
|
||||||
renderers
|
renderers,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
await this.getStunden();
|
await this.getStunden();
|
||||||
await this.getCompactibleEventTypes();
|
await this.getCompactibleEventTypes();
|
||||||
},
|
},
|
||||||
template: /* html */`
|
template: /* html */ `
|
||||||
<fhc-calendar
|
<fhc-calendar
|
||||||
ref="calendar"
|
ref="calendar"
|
||||||
class="fhc-calendar-lvplan"
|
class="fhc-calendar-lvplan"
|
||||||
@@ -203,5 +205,5 @@ export default {
|
|||||||
<template #actions>
|
<template #actions>
|
||||||
<slot />
|
<slot />
|
||||||
</template>
|
</template>
|
||||||
</fhc-calendar>`
|
</fhc-calendar>`,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,30 +1,36 @@
|
|||||||
import Pagination from "../../Pagination/Pagination.js";
|
import Pagination from '../../Pagination/Pagination.js';
|
||||||
import StudiengangInformation from "./StudiengangInformation/StudiengangInformation.js";
|
import StudiengangInformation from './StudiengangInformation/StudiengangInformation.js';
|
||||||
import BsConfirm from "../../Bootstrap/Confirm.js";
|
import BsConfirm from '../../Bootstrap/Confirm.js';
|
||||||
|
|
||||||
import ApiCms from "../../../api/factory/cms.js";
|
import ApiCms from '../../../api/factory/cms.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "NewsComponent",
|
name: 'NewsComponent',
|
||||||
components: {
|
components: {
|
||||||
Pagination,
|
Pagination,
|
||||||
StudiengangInformation,
|
StudiengangInformation,
|
||||||
},
|
},
|
||||||
inject: ["isMobile"],
|
inject: ['isMobile'],
|
||||||
|
props: {
|
||||||
|
permissions: Object,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
content: null,
|
content: null,
|
||||||
maxPageCount: 0,
|
maxPageCount: 0,
|
||||||
page_size: 10,
|
page_size: 100,
|
||||||
page: 1,
|
page: 1,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
"$p.user_language.value": function (sprache) {
|
'$p.user_language.value': function (sprache) {
|
||||||
this.fetchNews();
|
this.fetchNews();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
hasBasisNewsTypRPermission() {
|
||||||
|
return this.permissions['basis/news_r'] || false;
|
||||||
|
},
|
||||||
sprache: function () {
|
sprache: function () {
|
||||||
return this.$p.user_language.value;
|
return this.$p.user_language.value;
|
||||||
},
|
},
|
||||||
@@ -35,9 +41,10 @@ export default {
|
|||||||
ApiCms.getNews(this.page, this.page_size, this.sprache),
|
ApiCms.getNews(this.page, this.page_size, this.sprache),
|
||||||
);
|
);
|
||||||
this.content = newsResponse.data;
|
this.content = newsResponse.data;
|
||||||
|
this.maxPageCount = newsResponse.meta.row_count;
|
||||||
|
|
||||||
document.querySelectorAll("#cms [data-confirm]").forEach((el) => {
|
document.querySelectorAll('#cms [data-confirm]').forEach((el) => {
|
||||||
el.addEventListener("click", (evt) => {
|
el.addEventListener('click', (evt) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
BsConfirm.popup(el.dataset.confirm)
|
BsConfirm.popup(el.dataset.confirm)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -46,12 +53,12 @@ export default {
|
|||||||
// TODO(chris): check for success then show message and/or reload
|
// TODO(chris): check for success then show message and/or reload
|
||||||
location = location;
|
location = location;
|
||||||
})
|
})
|
||||||
.catch((err) => console.error("ERROR:", err));
|
.catch((err) => console.error('ERROR:', err));
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
document.querySelectorAll("#cms [data-href]").forEach((el) => {
|
document.querySelectorAll('#cms [data-href]').forEach((el) => {
|
||||||
el.href = el.dataset.href.replace(
|
el.href = el.dataset.href.replace(
|
||||||
/^ROOT\//,
|
/^ROOT\//,
|
||||||
FHC_JS_DATA_STORAGE_OBJECT.app_root,
|
FHC_JS_DATA_STORAGE_OBJECT.app_root,
|
||||||
@@ -73,48 +80,41 @@ export default {
|
|||||||
},
|
},
|
||||||
formatExternalHtml() {
|
formatExternalHtml() {
|
||||||
document
|
document
|
||||||
.querySelectorAll(".news-list-item .card-header")
|
.querySelectorAll('.news-list-item .card-header')
|
||||||
.forEach((el) => {
|
.forEach((el) => {
|
||||||
el.classList.add("fhc-primary");
|
el.classList.add('fhc-primary');
|
||||||
});
|
});
|
||||||
document.querySelectorAll(".news-list-item .row").forEach((el) => {
|
document.querySelectorAll('.news-list-item .row').forEach((el) => {
|
||||||
el.classList.add("w-100");
|
el.classList.add('w-100');
|
||||||
el.classList.add("align-items-center");
|
el.classList.add('align-items-center');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.news-list-item .row h2').forEach((el) => {
|
||||||
|
el.classList.add('mb-0');
|
||||||
});
|
});
|
||||||
document
|
|
||||||
.querySelectorAll(".news-list-item .row h2")
|
|
||||||
.forEach((el) => {
|
|
||||||
el.classList.add("mb-0");
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
afterPageUpdated(event) {
|
afterPageUpdated(event) {
|
||||||
this.page = event.page;
|
this.page = event.page;
|
||||||
this.page_size = event.rows;
|
this.page_size = event.rows;
|
||||||
this.$refs.newsPageHeading.scrollIntoView({block: 'end'});
|
this.$refs.newsPageHeading.scrollIntoView({ block: 'end' });
|
||||||
this.loadNewPageContent(event);
|
this.loadNewPageContent(event);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.fetchNews();
|
this.fetchNews();
|
||||||
|
|
||||||
this.$api
|
|
||||||
.call(ApiCms.getNewsRowCount())
|
|
||||||
.then((res) => res.data)
|
|
||||||
.then((result) => {
|
|
||||||
this.maxPageCount = result;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
template: /*html*/ `
|
template: /*html*/ `
|
||||||
<div :class="{'pb-3': isMobile}" class="overflow-x-hidden">
|
<div :class="{'pb-3': isMobile}" class="overflow-x-hidden">
|
||||||
<h2 ref="newsPageHeading" class="fhc-primary-color">News</h2>
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<h2 ref="newsPageHeading" class="fhc-primary-color">News</h2>
|
||||||
|
<router-link
|
||||||
|
v-if="hasBasisNewsTypRPermission"
|
||||||
|
class="btn btn-primary mb-3"
|
||||||
|
:to="{ name: 'NewsAdministration' }"
|
||||||
|
>
|
||||||
|
{{ $p.t('ui', 'newsAdministration') }} <i class="fas fa-cog m-0"></i>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<pagination
|
|
||||||
v-if="content?true:false"
|
|
||||||
:page="page"
|
|
||||||
:page_size="page_size"
|
|
||||||
@pageUpdated="afterPageUpdated($event)"
|
|
||||||
:maxPageCount="maxPageCount"
|
|
||||||
></pagination>
|
|
||||||
<div class="container-fluid mt-4">
|
<div class="container-fluid mt-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col" v-html="content">
|
<div class="col" v-html="content">
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
import NewsItemForm from './NewsItemForm.js';
|
||||||
|
import NewsItemPreview from './NewsItemPreview.js';
|
||||||
|
import NewsList from './NewsList.js';
|
||||||
|
import ApiNewsAdministration from '../../../../api/factory/newsAdministration.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewsAdministration',
|
||||||
|
components: {
|
||||||
|
NewsItemForm,
|
||||||
|
NewsItemPreview,
|
||||||
|
NewsList,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
permissions: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isNewsFormShown: false,
|
||||||
|
isNewsPreviewShown: false,
|
||||||
|
editableNewsItem: null,
|
||||||
|
newsPreview: null,
|
||||||
|
newsFormCollapse: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
isNewsFormShown: async function (isShown) {
|
||||||
|
await this.$nextTick();
|
||||||
|
this.newsFormCollapse?.[isShown ? 'show' : 'hide']();
|
||||||
|
},
|
||||||
|
'$route.query.newsId': {
|
||||||
|
immediate: true,
|
||||||
|
handler(newNewsId, oldNewsId) {
|
||||||
|
if (!newNewsId) {
|
||||||
|
this.isNewsFormShown = false;
|
||||||
|
this.isNewsPreviewShown = false;
|
||||||
|
this.editableNewsItem = null;
|
||||||
|
this.newsPreview = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editNewsItem({ newsId: this.$route.query.newsId });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasBasisNewsTypRPermission() {
|
||||||
|
return this.permissions['basis/news_r'] || false;
|
||||||
|
},
|
||||||
|
hasBasisNewsTypWPermission() {
|
||||||
|
return this.permissions['basis/news_w'] || false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async scrollToNewsForm() {
|
||||||
|
await this.$nextTick();
|
||||||
|
this.$refs.newsItemForm?.$el?.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async reloadNews() {
|
||||||
|
await this.$refs.newsList.fetchNews();
|
||||||
|
},
|
||||||
|
async showCreateNewsForm() {
|
||||||
|
this.editableNewsItem = null;
|
||||||
|
await this.$refs.newsItemForm?.fillFormData();
|
||||||
|
|
||||||
|
await this.$nextTick();
|
||||||
|
|
||||||
|
this.isNewsPreviewShown = false;
|
||||||
|
this.isNewsFormShown = true;
|
||||||
|
|
||||||
|
this.scrollToNewsForm();
|
||||||
|
},
|
||||||
|
async hideNewsForm() {
|
||||||
|
this.isNewsFormShown = false;
|
||||||
|
this.isNewsPreviewShown = false;
|
||||||
|
|
||||||
|
this.editableNewsItem = null;
|
||||||
|
await this.$refs.newsItemForm?.fillFormData();
|
||||||
|
|
||||||
|
this.newsPreview = null;
|
||||||
|
},
|
||||||
|
handleNewsItemSaved() {
|
||||||
|
this.isNewsFormShown = false;
|
||||||
|
this.isNewsPreviewShown = false;
|
||||||
|
this.editableNewsItem = null;
|
||||||
|
this.newsPreview = null;
|
||||||
|
this.reloadNews();
|
||||||
|
this.$refs.newsItemForm?.fillFormData();
|
||||||
|
},
|
||||||
|
async handleNewsItemUpdated() {
|
||||||
|
this.isNewsFormShown = false;
|
||||||
|
this.isNewsPreviewShown = false;
|
||||||
|
this.editableNewsItem = null;
|
||||||
|
this.newsPreview = null;
|
||||||
|
this.reloadNews();
|
||||||
|
this.$refs.newsItemForm?.fillFormData();
|
||||||
|
},
|
||||||
|
async loadNewsItem(newsId) {
|
||||||
|
if (!newsId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$api.call(
|
||||||
|
ApiNewsAdministration.getNewsItem(newsId),
|
||||||
|
);
|
||||||
|
const newsItem = response.data;
|
||||||
|
|
||||||
|
if (!newsItem) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newsItem;
|
||||||
|
} catch (error) {
|
||||||
|
this.$fhcAlert.handleSystemError(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async editNewsItem(newsItem) {
|
||||||
|
try {
|
||||||
|
this.editableNewsItem = await this.loadNewsItem(newsItem.newsId);
|
||||||
|
this.isNewsPreviewShown = false;
|
||||||
|
|
||||||
|
await this.$nextTick();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.isNewsFormShown = true;
|
||||||
|
this.scrollToNewsForm();
|
||||||
|
}, 100);
|
||||||
|
} catch (error) {
|
||||||
|
this.$fhcAlert.handleSystemError(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleNewsPreview() {
|
||||||
|
this.isNewsPreviewShown = !this.isNewsPreviewShown;
|
||||||
|
},
|
||||||
|
handlePreviewChange(preview) {
|
||||||
|
this.newsPreview = preview;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$p.loadCategory('ui').then(() => {
|
||||||
|
this.phrasesLoaded = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.newsFormCollapse = new bootstrap.Collapse(
|
||||||
|
this.$refs.newsFormCollapse,
|
||||||
|
{ toggle: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isNewsFormShown) {
|
||||||
|
this.newsFormCollapse.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.newsFormCollapse?.dispose();
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<div class="pb-3 overflow-x-hidden">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h2 ref="newsPageHeading" class="fhc-primary-color mb-0">{{ $capitalize($p.t('ui', 'newsAdministration')) }}</h2>
|
||||||
|
<button
|
||||||
|
v-if="!isNewsFormShown && hasBasisNewsTypWPermission"
|
||||||
|
@click="showCreateNewsForm"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary rounded-circle d-inline-flex align-items-center justify-content-center p-0"
|
||||||
|
style="width: 2.5rem; height: 2.5rem"
|
||||||
|
:title="$capitalize($p.t('ui', 'createNews'))"
|
||||||
|
:aria-label="$capitalize($p.t('ui', 'createNews'))"
|
||||||
|
aria-controls="news-item-form-collapse"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-plus" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div ref="newsFormCollapse" id="news-item-form-collapse" class="collapse">
|
||||||
|
<div class="row g-4 align-items-start">
|
||||||
|
<div :class="isNewsPreviewShown ? 'col-12 col-xl-6' : 'col-12'">
|
||||||
|
<news-item-form
|
||||||
|
ref="newsItemForm"
|
||||||
|
:key="editableNewsItem?.newsId || 'new-news-item'"
|
||||||
|
id="news-item-form"
|
||||||
|
:news-item="editableNewsItem"
|
||||||
|
:is-preview-shown="isNewsPreviewShown"
|
||||||
|
@created="handleNewsItemSaved"
|
||||||
|
@update="handleNewsItemUpdated"
|
||||||
|
@cancel="hideNewsForm"
|
||||||
|
@preview-change="handlePreviewChange"
|
||||||
|
@toggle-preview="toggleNewsPreview"
|
||||||
|
></news-item-form>
|
||||||
|
</div>
|
||||||
|
<div v-if="isNewsPreviewShown" class="col-12 col-xl-6">
|
||||||
|
<news-item-preview
|
||||||
|
:form-data="newsPreview?.formData"
|
||||||
|
:active-language="newsPreview?.activeLanguage"
|
||||||
|
></news-item-preview>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<news-list
|
||||||
|
v-if="hasBasisNewsTypRPermission"
|
||||||
|
ref="newsList"
|
||||||
|
@edit="editNewsItem"
|
||||||
|
></news-list>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
export default {
|
||||||
|
name: 'NewsItemAddLanguageTab',
|
||||||
|
emits: ['tab-action'],
|
||||||
|
props: {
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$p.loadCategory('ui').then(() => {
|
||||||
|
this.phrasesLoaded = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<div class="py-4 text-center text-muted">
|
||||||
|
<div v-if="config?.languages?.length" class="d-flex justify-content-center gap-2">
|
||||||
|
<button
|
||||||
|
v-for="language in config.languages"
|
||||||
|
:key="language.value"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-primary text-start"
|
||||||
|
@click="$emit('tab-action', { action: 'add-language', language: language.value })"
|
||||||
|
>
|
||||||
|
{{ language.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p v-else class="mb-0 text-muted">{{ $capitalize($p.t('ui', 'allLanguagesAdded')) }}</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
import FormInput from '../../../Form/Input.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewsItemContentForm',
|
||||||
|
components: {
|
||||||
|
FormInput,
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'tab-action'],
|
||||||
|
props: {
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
modelValue: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const translation = this.modelValue.find(
|
||||||
|
(item) => item.language === this.config.language,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
editor: null,
|
||||||
|
dmsFileBrowser: null,
|
||||||
|
dmsFileBrowserClosePollId: null,
|
||||||
|
dmsFileBrowserMessageHandler: null,
|
||||||
|
contentData: {
|
||||||
|
language: this.config.language,
|
||||||
|
author: '',
|
||||||
|
title: '',
|
||||||
|
text: '',
|
||||||
|
isPublished: false,
|
||||||
|
...translation,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
contentData: {
|
||||||
|
deep: true,
|
||||||
|
handler(contentData) {
|
||||||
|
const translations = this.modelValue.map((item) => ({ ...item }));
|
||||||
|
const index = translations.findIndex(
|
||||||
|
(item) => item.language === this.config.language,
|
||||||
|
);
|
||||||
|
const translation = {
|
||||||
|
...contentData,
|
||||||
|
language: this.config.language,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
translations.push(translation);
|
||||||
|
} else {
|
||||||
|
translations[index] = translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('update:modelValue', translations);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
cleanupDmsFileBrowser(closePopup = false) {
|
||||||
|
if (this.dmsFileBrowserClosePollId !== null) {
|
||||||
|
window.clearInterval(this.dmsFileBrowserClosePollId);
|
||||||
|
this.dmsFileBrowserClosePollId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.dmsFileBrowserMessageHandler) {
|
||||||
|
window.removeEventListener(
|
||||||
|
'message',
|
||||||
|
this.dmsFileBrowserMessageHandler,
|
||||||
|
);
|
||||||
|
this.dmsFileBrowserMessageHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closePopup && this.dmsFileBrowser && !this.dmsFileBrowser.closed) {
|
||||||
|
this.dmsFileBrowser.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dmsFileBrowser = null;
|
||||||
|
},
|
||||||
|
openDmsFileBrowser(callback, meta) {
|
||||||
|
this.cleanupDmsFileBrowser(true);
|
||||||
|
|
||||||
|
const appRoot =
|
||||||
|
FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/\/+$/, '') + '/';
|
||||||
|
const fileType = meta && meta.filetype ? meta.filetype : 'file';
|
||||||
|
const fileBrowserUrl =
|
||||||
|
appRoot + 'cms/tinymce_dms.php?type=' + encodeURIComponent(fileType);
|
||||||
|
const popupName = this.editor
|
||||||
|
? 'FHCFileBrowser_' + this.editor.id
|
||||||
|
: 'FHCFileBrowser';
|
||||||
|
const popup = window.open(
|
||||||
|
fileBrowserUrl,
|
||||||
|
popupName,
|
||||||
|
'width=750,height=550,resizable=yes,scrollbars=yes',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!popup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dmsFileBrowser = popup;
|
||||||
|
|
||||||
|
this.dmsFileBrowserMessageHandler = (event) => {
|
||||||
|
const message = event.data;
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.origin !== window.location.origin ||
|
||||||
|
event.source !== popup ||
|
||||||
|
this.dmsFileBrowser !== popup ||
|
||||||
|
!message ||
|
||||||
|
message.type !== 'fhcomplete:dms:selected' ||
|
||||||
|
!Number.isInteger(message.dmsId) ||
|
||||||
|
message.dmsId <= 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
callback(
|
||||||
|
appRoot + 'cms/dms.php?id=' + encodeURIComponent(message.dmsId),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.cleanupDmsFileBrowser(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('message', this.dmsFileBrowserMessageHandler);
|
||||||
|
|
||||||
|
this.dmsFileBrowserClosePollId = window.setInterval(() => {
|
||||||
|
if (this.dmsFileBrowser === popup && popup.closed) {
|
||||||
|
this.cleanupDmsFileBrowser();
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
popup.focus();
|
||||||
|
},
|
||||||
|
initTinyMCE() {
|
||||||
|
const vm = this;
|
||||||
|
tinymce.init({
|
||||||
|
target: this.$refs.editor.$refs.input, // Important: enables multiple component instances
|
||||||
|
height: 500,
|
||||||
|
toolbar:
|
||||||
|
'styleselect fontsizeselect | bold italic underline | alignleft aligncenter alignright alignjustify | link unlink image | bullist | pastetext',
|
||||||
|
plugins: 'lists link image paste',
|
||||||
|
paste_as_text: true,
|
||||||
|
statusbar: true,
|
||||||
|
resize: true,
|
||||||
|
file_picker_types: 'file image',
|
||||||
|
file_picker_callback: (callback, _value, meta) => {
|
||||||
|
vm.openDmsFileBrowser(callback, meta);
|
||||||
|
},
|
||||||
|
|
||||||
|
style_formats: [
|
||||||
|
{ title: vm.$capitalize(vm.$p.t('ui', 'blocks')), block: 'div' },
|
||||||
|
{ title: vm.$capitalize(vm.$p.t('ui', 'paragraph')), block: 'p' },
|
||||||
|
{ title: vm.$capitalize(vm.$p.t('ui', 'heading1')), block: 'h1' },
|
||||||
|
{ title: vm.$capitalize(vm.$p.t('ui', 'heading2')), block: 'h2' },
|
||||||
|
{ title: vm.$capitalize(vm.$p.t('ui', 'heading3')), block: 'h3' },
|
||||||
|
{ title: vm.$capitalize(vm.$p.t('ui', 'heading4')), block: 'h4' },
|
||||||
|
{ title: vm.$capitalize(vm.$p.t('ui', 'heading5')), block: 'h5' },
|
||||||
|
{ title: vm.$capitalize(vm.$p.t('ui', 'heading6')), block: 'h6' },
|
||||||
|
],
|
||||||
|
setup: (editor) => {
|
||||||
|
vm.editor = editor;
|
||||||
|
|
||||||
|
editor.on('input change undo redo', () => {
|
||||||
|
vm.contentData.text = editor.getContent();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.$p.loadCategory(['global', 'ui', 'notiz']);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.$refs.editor?.$refs.input) {
|
||||||
|
this.initTinyMCE();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.cleanupDmsFileBrowser(true);
|
||||||
|
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<div class="position-relative overflow-x-hidden">
|
||||||
|
<button
|
||||||
|
v-if="config.language !== 'German'"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-outline-danger position-absolute top-0 end-0"
|
||||||
|
:title="$capitalize($p.t('ui', 'removeLanguage'))"
|
||||||
|
:aria-label="$capitalize($p.t('ui', 'removeLanguage'))"
|
||||||
|
@click="$emit('tab-action', { action: 'remove-language', language: config.language })"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-trash-can" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<div class="d-flex flex-column flex-md-row align-items-md-end gap-3">
|
||||||
|
<form-input
|
||||||
|
type="text"
|
||||||
|
:label="$capitalize($p.t('notiz', 'verfasser'))"
|
||||||
|
name="author"
|
||||||
|
v-model="contentData.author"
|
||||||
|
>
|
||||||
|
</form-input>
|
||||||
|
<form-input
|
||||||
|
type="text"
|
||||||
|
:label="$capitalize($p.t('global', 'betreff'))"
|
||||||
|
name="title"
|
||||||
|
v-model="contentData.title"
|
||||||
|
>
|
||||||
|
</form-input>
|
||||||
|
<form-input
|
||||||
|
type="checkbox"
|
||||||
|
:label="$capitalize($p.t('ui', 'publish'))"
|
||||||
|
name="isPublished"
|
||||||
|
v-model="contentData.isPublished"
|
||||||
|
>
|
||||||
|
</form-input>
|
||||||
|
</div>
|
||||||
|
<form-input
|
||||||
|
ref="editor"
|
||||||
|
:label="$capitalize($p.t('global', 'text') + ' *')"
|
||||||
|
type="textarea"
|
||||||
|
name="text"
|
||||||
|
v-model="contentData.text"
|
||||||
|
rows="5"
|
||||||
|
cols="75"
|
||||||
|
style="min-height: 500px"
|
||||||
|
>
|
||||||
|
</form-input>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import BootstrapModal from '../../../Bootstrap/Modal.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewsItemCopyTranslationModal',
|
||||||
|
components: {
|
||||||
|
BootstrapModal,
|
||||||
|
},
|
||||||
|
emits: ['copy', 'hidden'],
|
||||||
|
props: {
|
||||||
|
sourceLanguages: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
targetLanguageLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
targetHasContent: Boolean,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedSourceLanguage: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show() {
|
||||||
|
this.selectedSourceLanguage = null;
|
||||||
|
this.$refs.modal.show();
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
this.$refs.modal.hide();
|
||||||
|
},
|
||||||
|
selectSourceLanguage(language) {
|
||||||
|
if (this.targetHasContent) {
|
||||||
|
this.selectedSourceLanguage = language;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.copyTranslation(language);
|
||||||
|
},
|
||||||
|
copyTranslation(language = this.selectedSourceLanguage) {
|
||||||
|
const sourceLanguage = language?.value ?? language;
|
||||||
|
|
||||||
|
if (!sourceLanguage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('copy', sourceLanguage);
|
||||||
|
this.hide();
|
||||||
|
},
|
||||||
|
resetSelection() {
|
||||||
|
this.selectedSourceLanguage = null;
|
||||||
|
this.$emit('hidden');
|
||||||
|
},
|
||||||
|
getCopyingTranslationText() {
|
||||||
|
return this.$capitalize(
|
||||||
|
this.$p.t('ui', 'copyingTranslation', {
|
||||||
|
source: this.selectedSourceLanguage?.label ?? '',
|
||||||
|
target: this.targetLanguageLabel,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getChooseLanguageText() {
|
||||||
|
return this.$capitalize(
|
||||||
|
this.$p.t('ui', 'chooseLanguageToCopyContent', {
|
||||||
|
target: this.targetLanguageLabel,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$p.loadCategory('ui').then(() => {
|
||||||
|
this.phrasesLoaded = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<bootstrap-modal
|
||||||
|
ref="modal"
|
||||||
|
dialog-class="modal-dialog-centered"
|
||||||
|
body-class="p-4"
|
||||||
|
@hidden-bs-modal="resetSelection"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
{{ $capitalize(selectedSourceLanguage ? $p.t('ui', 'overwriteTranslationContent') : $p.t('ui', 'copyTranslationContent')) }}
|
||||||
|
</template>
|
||||||
|
<template v-if="selectedSourceLanguage">
|
||||||
|
<p class="mb-3">
|
||||||
|
{{ getCopyingTranslationText() }}
|
||||||
|
</p>
|
||||||
|
<p class="text-muted small">{{ $capitalize($p.t('ui', 'existingContentWillBeLost')) }}</p>
|
||||||
|
<div class="d-flex justify-content-end gap-2 mt-4">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" @click="selectedSourceLanguage = null">
|
||||||
|
{{ $capitalize($p.t('ui', 'chooseAnotherLanguage')) }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary" @click="copyTranslation()">
|
||||||
|
{{ $capitalize($p.t('ui', 'overwriteAndCopy')) }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<p class="text-muted">{{ getChooseLanguageText() }}</p>
|
||||||
|
<div v-if="sourceLanguages.length" class="d-grid gap-2">
|
||||||
|
<button
|
||||||
|
v-for="language in sourceLanguages"
|
||||||
|
:key="language.value"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-primary text-start"
|
||||||
|
@click="selectSourceLanguage(language)"
|
||||||
|
>
|
||||||
|
{{ language.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p v-else class="mb-0 text-muted">{{ $capitalize($p.t('ui', 'addLanguageBeforeCopying')) }}</p>
|
||||||
|
</template>
|
||||||
|
</bootstrap-modal>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,614 @@
|
|||||||
|
import CoreForm from '../../../Form/Form.js';
|
||||||
|
import FormInput from '../../../Form/Input.js';
|
||||||
|
import FhcTabs from '../../../Tabs.js';
|
||||||
|
import NewsItemCopyTranslationModal from './NewsItemCopyTranslationModal.js';
|
||||||
|
import ApiStudiengang from '../../../../api/factory/studiengang.js';
|
||||||
|
import ApiNewsAdministration from '../../../../api/factory/newsAdministration.js';
|
||||||
|
|
||||||
|
const BASE_COMPONENT_URL =
|
||||||
|
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||||
|
'public/js/components/Cis/Cms/NewsAdministration/';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewsItemForm',
|
||||||
|
components: {
|
||||||
|
CoreForm,
|
||||||
|
FormInput,
|
||||||
|
FhcTabs,
|
||||||
|
NewsItemCopyTranslationModal,
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
'created',
|
||||||
|
'update',
|
||||||
|
'cancel',
|
||||||
|
'translation-copied',
|
||||||
|
'preview-change',
|
||||||
|
'toggle-preview',
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
newsItem: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
isPreviewShown: Boolean,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
degreePrograms: [],
|
||||||
|
filteredDegreePrograms: [],
|
||||||
|
isSaving: false,
|
||||||
|
activeContentFormKey: 'germanContentForm',
|
||||||
|
contentFormRenderKey: 0,
|
||||||
|
formData: {
|
||||||
|
visibleFrom: null,
|
||||||
|
visibleTo: null,
|
||||||
|
degreeProgram: null,
|
||||||
|
semester: null,
|
||||||
|
translations: [],
|
||||||
|
},
|
||||||
|
contentFormItems: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$p.user_language.value': function () {
|
||||||
|
Object.values(this.contentFormItems).forEach((item) => {
|
||||||
|
if (item.config?.language) {
|
||||||
|
item.title = this.getContentFormTitle(item.config.language);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formData: {
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
handler() {
|
||||||
|
this.emitPreviewChange();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
activeContentLanguage() {
|
||||||
|
this.emitPreviewChange();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sprache: function () {
|
||||||
|
return this.$p.user_language.value;
|
||||||
|
},
|
||||||
|
availableLanguages() {
|
||||||
|
this.sprache;
|
||||||
|
|
||||||
|
return FHC_JS_DATA_STORAGE_OBJECT?.server_languages?.map(
|
||||||
|
(languageObject) => ({
|
||||||
|
label: this.getLanguageLabel(languageObject.sprache),
|
||||||
|
value: languageObject.sprache,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
semesters() {
|
||||||
|
this.sprache;
|
||||||
|
const semesterLabel = this.$capitalize(this.$p.t('lehre', 'semester'));
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$capitalize(this.$p.t('ui', 'all_semester')),
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
...Array.from({ length: 8 }, (_, index) => ({
|
||||||
|
label: `${index + 1}. ${semesterLabel}`,
|
||||||
|
value: String(index + 1),
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
dropdownParsedDegreePrograms() {
|
||||||
|
return this.degreePrograms
|
||||||
|
.map((degreeProgram) => {
|
||||||
|
let labelFragment = degreeProgram.typ + degreeProgram.kurzbz;
|
||||||
|
labelFragment = labelFragment.trim().toUpperCase();
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: `${labelFragment} (${degreeProgram.bezeichnung})`,
|
||||||
|
value: degreeProgram.studiengang_kz,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
|
},
|
||||||
|
visibleContentLanguages() {
|
||||||
|
return Object.values(this.contentFormItems)
|
||||||
|
.map((item) => item.config?.language)
|
||||||
|
.filter(Boolean);
|
||||||
|
},
|
||||||
|
languagesToAdd() {
|
||||||
|
return this.availableLanguages.filter(
|
||||||
|
(language) => !this.visibleContentLanguages.includes(language.value),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
activeContentLanguage() {
|
||||||
|
return this.contentFormItems[this.activeContentFormKey]?.config?.language;
|
||||||
|
},
|
||||||
|
copySourceLanguages() {
|
||||||
|
return this.visibleContentLanguages
|
||||||
|
.filter((language) => language !== this.activeContentLanguage)
|
||||||
|
.map((language) => ({
|
||||||
|
label: this.getLanguageLabel(language),
|
||||||
|
value: language,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
activeTranslationHasContent() {
|
||||||
|
return this.hasTranslationContent(
|
||||||
|
this.getTranslation(this.activeContentLanguage),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
emitPreviewChange() {
|
||||||
|
this.$emit('preview-change', {
|
||||||
|
formData: this.formData,
|
||||||
|
activeLanguage: this.activeContentLanguage,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getContentFormKey(language) {
|
||||||
|
return `${language.toLowerCase()}ContentForm`;
|
||||||
|
},
|
||||||
|
createContentFormItem(language) {
|
||||||
|
const key = this.getContentFormKey(language);
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: this.getContentFormTitle(language),
|
||||||
|
component: BASE_COMPONENT_URL + 'NewsItemContentForm.js?124',
|
||||||
|
config: {
|
||||||
|
language,
|
||||||
|
type: 'news',
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
createAddLanguageTab() {
|
||||||
|
return {
|
||||||
|
title: '+',
|
||||||
|
component: BASE_COMPONENT_URL + 'NewsItemAddLanguageTab.js',
|
||||||
|
config: { languages: [] },
|
||||||
|
key: 'addLanguage',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
showCopyTranslationModal() {
|
||||||
|
this.$refs.copyTranslationModal.show();
|
||||||
|
},
|
||||||
|
getLanguageLabel(language) {
|
||||||
|
const languagePhrases = {
|
||||||
|
German: ['global', 'deutsch'],
|
||||||
|
English: ['global', 'englisch'],
|
||||||
|
French: ['ui', 'franzoesisch'],
|
||||||
|
Spanish: ['ui', 'spanisch'],
|
||||||
|
};
|
||||||
|
const phrase = languagePhrases[language];
|
||||||
|
|
||||||
|
return phrase
|
||||||
|
? this.$capitalize(this.$p.t(phrase[0], phrase[1]))
|
||||||
|
: language;
|
||||||
|
},
|
||||||
|
getContentFormTitle(language) {
|
||||||
|
return this.$capitalize(
|
||||||
|
this.$p.t('ui', 'contentFormTitle', {
|
||||||
|
language: this.getLanguageLabel(language),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getTranslation(language) {
|
||||||
|
return this.formData.translations.find(
|
||||||
|
(translation) => translation.language === language,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
hasTranslationContent(translation) {
|
||||||
|
return ['author', 'title', 'text'].some(
|
||||||
|
(field) => String(translation?.[field] ?? '').trim() !== '',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
copyTranslation(sourceLanguage) {
|
||||||
|
const sourceTranslation = this.getTranslation(sourceLanguage);
|
||||||
|
const targetTranslation = this.getTranslation(this.activeContentLanguage);
|
||||||
|
|
||||||
|
if (!sourceTranslation || !targetTranslation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(targetTranslation, {
|
||||||
|
author: sourceTranslation.author,
|
||||||
|
title: sourceTranslation.title,
|
||||||
|
text: sourceTranslation.text,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.contentFormRenderKey += 1;
|
||||||
|
this.$emit('translation-copied', {
|
||||||
|
from: sourceLanguage,
|
||||||
|
to: this.activeContentLanguage,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleContentTabChange(key) {
|
||||||
|
this.activeContentFormKey = key;
|
||||||
|
},
|
||||||
|
handleContentTabAction({ key, payload }) {
|
||||||
|
if (payload?.action === 'add-language') {
|
||||||
|
this.addLanguage(payload.language);
|
||||||
|
} else if (payload?.action === 'remove-language') {
|
||||||
|
this.removeLanguage(payload.language);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeLanguage(language) {
|
||||||
|
const key = this.getContentFormKey(language);
|
||||||
|
|
||||||
|
if (!this.contentFormItems[key]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentFormItems = { ...this.contentFormItems };
|
||||||
|
const addLanguageTab = contentFormItems.addLanguage;
|
||||||
|
delete contentFormItems[key];
|
||||||
|
delete contentFormItems.addLanguage;
|
||||||
|
|
||||||
|
const remainingLanguages = Object.values(contentFormItems)
|
||||||
|
.map((item) => item.config?.language)
|
||||||
|
.filter(Boolean);
|
||||||
|
contentFormItems.addLanguage = {
|
||||||
|
...addLanguageTab,
|
||||||
|
config: {
|
||||||
|
hasAvailableLanguages: this.availableLanguages.some(
|
||||||
|
(availableLanguage) =>
|
||||||
|
!remainingLanguages.includes(availableLanguage.value),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.contentFormItems = contentFormItems;
|
||||||
|
this.formData.translations = this.formData.translations.filter(
|
||||||
|
(translation) => translation.language !== language,
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextContentFormKey = Object.keys(contentFormItems).find(
|
||||||
|
(itemKey) => itemKey !== 'addLanguage',
|
||||||
|
);
|
||||||
|
this.activeContentFormKey = nextContentFormKey ?? 'addLanguage';
|
||||||
|
|
||||||
|
if (nextContentFormKey) {
|
||||||
|
this.$nextTick(() => this.$refs.tabs.change(nextContentFormKey));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addLanguage(language) {
|
||||||
|
const key = this.getContentFormKey(language);
|
||||||
|
|
||||||
|
if (this.contentFormItems[key]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addLanguageTab = this.contentFormItems.addLanguage;
|
||||||
|
const contentFormItems = { ...this.contentFormItems };
|
||||||
|
delete contentFormItems.addLanguage;
|
||||||
|
contentFormItems[key] = this.createContentFormItem(language);
|
||||||
|
contentFormItems.addLanguage = {
|
||||||
|
...addLanguageTab,
|
||||||
|
config: {
|
||||||
|
hasAvailableLanguages: this.availableLanguages.some(
|
||||||
|
(availableLanguage) =>
|
||||||
|
availableLanguage.value !== language &&
|
||||||
|
!this.visibleContentLanguages.includes(availableLanguage.value),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.contentFormItems = contentFormItems;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.formData.translations.some((item) => item.language === language)
|
||||||
|
) {
|
||||||
|
this.formData.translations.push({
|
||||||
|
language,
|
||||||
|
author: '',
|
||||||
|
title: '',
|
||||||
|
text: '',
|
||||||
|
isPublished: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeContentFormKey = key;
|
||||||
|
this.$nextTick(() => this.$refs.tabs.change(key));
|
||||||
|
},
|
||||||
|
restoreActiveContentTab() {
|
||||||
|
this.$refs.tabs.change(this.activeContentFormKey);
|
||||||
|
},
|
||||||
|
fillFormData(newsItem) {
|
||||||
|
this.formData.visibleFrom =
|
||||||
|
newsItem?.visibleFrom ?? newsItem?.dateTime?.slice(0, 10) ?? null;
|
||||||
|
this.formData.visibleTo = newsItem?.visibleTo ?? null;
|
||||||
|
const degreeProgramShortCode =
|
||||||
|
newsItem?.degreeProgramShortCode ??
|
||||||
|
newsItem?.degreeProgram?.value ??
|
||||||
|
null;
|
||||||
|
this.formData.degreeProgram =
|
||||||
|
this.dropdownParsedDegreePrograms.find(
|
||||||
|
(degreeProgram) =>
|
||||||
|
String(degreeProgram.value) === String(degreeProgramShortCode),
|
||||||
|
) ??
|
||||||
|
(degreeProgramShortCode === null
|
||||||
|
? null
|
||||||
|
: {
|
||||||
|
label: String(degreeProgramShortCode),
|
||||||
|
value: degreeProgramShortCode,
|
||||||
|
});
|
||||||
|
this.formData.semester = newsItem?.semester ?? null;
|
||||||
|
|
||||||
|
const translations = newsItem?.translations ?? [
|
||||||
|
{
|
||||||
|
language: newsItem?.language ?? 'German',
|
||||||
|
author: newsItem?.author ?? '',
|
||||||
|
title: newsItem?.title ?? '',
|
||||||
|
text: newsItem?.content ?? '',
|
||||||
|
isPublished: Boolean(newsItem?.isPublished),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
this.formData.translations = translations.map((sourceTranslation) => ({
|
||||||
|
language: sourceTranslation.language,
|
||||||
|
author: sourceTranslation.author ?? '',
|
||||||
|
title: sourceTranslation.title ?? '',
|
||||||
|
text: sourceTranslation.text ?? '',
|
||||||
|
isPublished: Boolean(sourceTranslation.isPublished),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const contentFormItems = {};
|
||||||
|
|
||||||
|
this.formData.translations.forEach((translation) => {
|
||||||
|
if (translation.language) {
|
||||||
|
contentFormItems[this.getContentFormKey(translation.language)] =
|
||||||
|
this.createContentFormItem(translation.language);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
contentFormItems.addLanguage = this.createAddLanguageTab();
|
||||||
|
this.contentFormItems = contentFormItems;
|
||||||
|
this.activeContentFormKey =
|
||||||
|
Object.keys(contentFormItems).find((key) => key !== 'addLanguage') ??
|
||||||
|
'addLanguage';
|
||||||
|
},
|
||||||
|
filterDegreePrograms(event) {
|
||||||
|
const query = event.query.toLowerCase();
|
||||||
|
|
||||||
|
return (this.filteredDegreePrograms =
|
||||||
|
this.dropdownParsedDegreePrograms.filter((unit) => {
|
||||||
|
return unit.label.toLowerCase().includes(query);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
async storeNewsItem() {
|
||||||
|
if (this.isSaving) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsedFormData = JSON.parse(JSON.stringify(this.formData));
|
||||||
|
parsedFormData.degreeProgramShortCode =
|
||||||
|
parsedFormData.degreeProgram?.value;
|
||||||
|
delete parsedFormData.degreeProgram;
|
||||||
|
|
||||||
|
this.isSaving = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$refs.form.call(
|
||||||
|
ApiNewsAdministration.storeNewsItem(parsedFormData),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.meta.status !== 'success') {
|
||||||
|
this.$fhcAlert.alertError(
|
||||||
|
this.$capitalize(this.$p.t('ui', 'fehlerBeimSpeichern')),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$fhcAlert.alertSuccess(
|
||||||
|
this.$capitalize(this.$p.t('ui', 'gespeichert')),
|
||||||
|
);
|
||||||
|
this.$emit('created');
|
||||||
|
} catch (error) {
|
||||||
|
if (!error?.handled) {
|
||||||
|
this.$fhcAlert.handleSystemError(error);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.isSaving = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async updateNewsItem() {
|
||||||
|
if (!this.newsItem || this.isSaving) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsedFormData = JSON.parse(JSON.stringify(this.formData));
|
||||||
|
parsedFormData.degreeProgramShortCode =
|
||||||
|
parsedFormData.degreeProgram?.value;
|
||||||
|
delete parsedFormData.degreeProgram;
|
||||||
|
|
||||||
|
this.isSaving = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$refs.form.call(
|
||||||
|
ApiNewsAdministration.updateNewsItem(
|
||||||
|
this.newsItem.newsId,
|
||||||
|
parsedFormData,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.meta.status !== 'success') {
|
||||||
|
this.$fhcAlert.alertError(
|
||||||
|
this.$capitalize(this.$p.t('ui', 'fehlerBeimSpeichern')),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$fhcAlert.alertSuccess(
|
||||||
|
this.$capitalize(this.$p.t('ui', 'gespeichert')),
|
||||||
|
);
|
||||||
|
this.$emit('update');
|
||||||
|
} catch (error) {
|
||||||
|
if (!error?.handled) {
|
||||||
|
this.$fhcAlert.handleSystemError(error);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.isSaving = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
await this.$p.loadCategory(['global', 'ui', 'lehre']);
|
||||||
|
|
||||||
|
let getAllDegreePrograms = await this.$api.call(
|
||||||
|
ApiStudiengang.getDegreePrograms(),
|
||||||
|
);
|
||||||
|
if (getAllDegreePrograms.meta.status === 'success') {
|
||||||
|
this.degreePrograms = getAllDegreePrograms.data.sort((a, b) =>
|
||||||
|
a.bezeichnung.localeCompare(b.bezeichnung),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.$fhcAlert.alertError(
|
||||||
|
this.$capitalize(this.$p.t('ui', 'fehlerBeimLesen')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fillFormData(this.newsItem);
|
||||||
|
|
||||||
|
if (!this.$props.newsItem) {
|
||||||
|
this.formData.degreeProgram =
|
||||||
|
this.dropdownParsedDegreePrograms.find(
|
||||||
|
(degreeProgram) => degreeProgram.value === 0,
|
||||||
|
) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addLanguageTab = this.contentFormItems.addLanguage;
|
||||||
|
if (addLanguageTab?.config) {
|
||||||
|
addLanguageTab.config.languages = this.languagesToAdd;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<section
|
||||||
|
class="card bg-white shadow-sm border-0 mb-4 overflow-x-hidden"
|
||||||
|
aria-labelledby="news-item-form-heading"
|
||||||
|
>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-between align-items-center gap-3 mb-3">
|
||||||
|
<h4 id="news-item-form-heading" ref="newsPageHeading" class="card-title fhc-primary-color mb-0">{{ $capitalize($p.t('ui', 'newsForm')) }}</h4>
|
||||||
|
<button
|
||||||
|
@click="$emit('toggle-preview')"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-outline-primary d-inline-flex align-items-center justify-content-center"
|
||||||
|
:title="$capitalize(isPreviewShown ? $p.t('ui', 'hidePreview') : $p.t('ui', 'showPreview'))"
|
||||||
|
:aria-label="$capitalize(isPreviewShown ? $p.t('ui', 'hidePreview') : $p.t('ui', 'showPreview'))"
|
||||||
|
:aria-expanded="isPreviewShown"
|
||||||
|
aria-controls="news-item-preview"
|
||||||
|
>
|
||||||
|
<i class="fa-solid" :class="isPreviewShown ? 'fa-eye-slash' : 'fa-eye'" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<core-form ref="form">
|
||||||
|
<div class="d-flex flex-row flex-md-row align-items-md-end gap-3">
|
||||||
|
<div class="d-flex flex-row flex-md-row align-items-md-end gap-3">
|
||||||
|
<form-input
|
||||||
|
:label="$capitalize($p.t('ui', 'visible') + ' ' + $p.t('ui', 'von'))"
|
||||||
|
:teleport="true"
|
||||||
|
:enable-time-picker="false"
|
||||||
|
type="datePicker"
|
||||||
|
name="visibleFrom"
|
||||||
|
v-model="formData.visibleFrom"
|
||||||
|
model-type="yyyy-MM-dd"
|
||||||
|
format="dd.MM.yyyy"
|
||||||
|
auto-apply
|
||||||
|
/>
|
||||||
|
<form-input
|
||||||
|
:label="$capitalize($p.t('ui', 'visible') + ' ' + $p.t('global', 'bis'))"
|
||||||
|
:teleport="true"
|
||||||
|
:enable-time-picker="false"
|
||||||
|
type="datePicker"
|
||||||
|
name="visibleTo"
|
||||||
|
v-model="formData.visibleTo"
|
||||||
|
model-type="yyyy-MM-dd"
|
||||||
|
format="dd.MM.yyyy"
|
||||||
|
auto-apply
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-row flex-md-row align-items-md-end gap-3">
|
||||||
|
<form-input
|
||||||
|
:label="$capitalize($p.t('lehre/studiengang'))"
|
||||||
|
:suggestions="filteredDegreePrograms"
|
||||||
|
:optionValue="(option) => option.value"
|
||||||
|
:optionLabel="(option) => option.label"
|
||||||
|
@complete="filterDegreePrograms($event)"
|
||||||
|
type="autocomplete"
|
||||||
|
name="degreeProgramShortCode"
|
||||||
|
v-model="formData.degreeProgram"
|
||||||
|
dropdown
|
||||||
|
forceSelection
|
||||||
|
>
|
||||||
|
</form-input>
|
||||||
|
<form-input
|
||||||
|
v-model="formData.semester"
|
||||||
|
:label="$capitalize($p.t('lehre/studiensemester'))"
|
||||||
|
type="select"
|
||||||
|
name="semester"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="semester in semesters"
|
||||||
|
:key="semester.value"
|
||||||
|
:value="semester.value"
|
||||||
|
>
|
||||||
|
{{ semester.label }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fhc-tabs
|
||||||
|
:key="contentFormRenderKey"
|
||||||
|
ref="tabs"
|
||||||
|
:useprimevue="true"
|
||||||
|
:config="contentFormItems"
|
||||||
|
:default="activeContentFormKey"
|
||||||
|
v-model="formData.translations"
|
||||||
|
@change="handleContentTabChange"
|
||||||
|
@tab-action="handleContentTabAction"
|
||||||
|
style="flex: 1 1 0%; height: 0%"
|
||||||
|
class="mt-3"
|
||||||
|
>
|
||||||
|
</fhc-tabs>
|
||||||
|
<div class="d-flex justify-content-end mt-2">
|
||||||
|
<button
|
||||||
|
v-if="activeContentLanguage && copySourceLanguages.length"
|
||||||
|
@click="showCopyTranslationModal"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-outline-secondary"
|
||||||
|
:title="$capitalize($p.t('ui', 'copyContentFromAnotherLanguage'))"
|
||||||
|
aria-haspopup="dialog"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-copy me-1" aria-hidden="true"></i>
|
||||||
|
{{ $capitalize($p.t('ui', 'copyContentFromAnotherLanguage')) }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<news-item-copy-translation-modal
|
||||||
|
ref="copyTranslationModal"
|
||||||
|
:source-languages="copySourceLanguages"
|
||||||
|
:target-language-label="getLanguageLabel(activeContentLanguage)"
|
||||||
|
:target-has-content="activeTranslationHasContent"
|
||||||
|
@copy="copyTranslation"
|
||||||
|
></news-item-copy-translation-modal>
|
||||||
|
<div v-if="!isSaving" class="d-flex justify-content-end gap-2 mt-4 pt-3 border-top">
|
||||||
|
<button
|
||||||
|
@click="$emit('cancel')"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
>
|
||||||
|
{{ $capitalize($p.t('ui', 'abbrechen')) }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="newsItem ? updateNewsItem() : storeNewsItem()"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
>
|
||||||
|
{{ $capitalize($p.t('global', 'speichern')) }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-else class="d-flex justify-content-end mt-4 pt-3 border-top" role="status">
|
||||||
|
<span class="spinner-border" aria-hidden="true"></span>
|
||||||
|
</div>
|
||||||
|
</core-form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import FormInput from '../../../Form/Input.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewsItemPreview',
|
||||||
|
components: {
|
||||||
|
FormInput,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
formData: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
activeLanguage: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedLanguage: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sprache() {
|
||||||
|
return this.$p.user_language.value;
|
||||||
|
},
|
||||||
|
availableLanguages() {
|
||||||
|
return (this.formData?.translations ?? [])
|
||||||
|
.map((translation) => translation.language)
|
||||||
|
.filter(Boolean);
|
||||||
|
},
|
||||||
|
displayedLanguage() {
|
||||||
|
return this.selectedLanguage || this.activeLanguage;
|
||||||
|
},
|
||||||
|
translation() {
|
||||||
|
return this.formData?.translations?.find(
|
||||||
|
(item) => item.language === this.displayedLanguage,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
parsedTranslationText() {
|
||||||
|
const text = this.translation?.text ?? '';
|
||||||
|
if (!text) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const appRoot = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/\/+$/, '');
|
||||||
|
|
||||||
|
return text.replace(
|
||||||
|
/(?:https?:\/\/|\/|\.\.\/)?[^"'<>\s]*?dms\.php(\?[^"'<>\s]*)/gi,
|
||||||
|
`${appRoot}/cms/dms.php$1`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
activeLanguage: {
|
||||||
|
immediate: true,
|
||||||
|
handler(language) {
|
||||||
|
if (this.availableLanguages.includes(language)) {
|
||||||
|
this.selectedLanguage = language;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
availableLanguages(languages) {
|
||||||
|
if (languages.includes(this.selectedLanguage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedLanguage = languages.includes(this.activeLanguage)
|
||||||
|
? this.activeLanguage
|
||||||
|
: (languages[0] ?? '');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$p.loadCategory(['global', 'ui']).then(() => {
|
||||||
|
this.phrasesLoaded = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getLanguageLabel(language) {
|
||||||
|
this.sprache;
|
||||||
|
const languagePhrases = {
|
||||||
|
German: ['global', 'deutsch'],
|
||||||
|
English: ['global', 'englisch'],
|
||||||
|
French: ['ui', 'franzoesisch'],
|
||||||
|
Spanish: ['ui', 'spanisch'],
|
||||||
|
};
|
||||||
|
const phrase = languagePhrases[language];
|
||||||
|
|
||||||
|
return phrase
|
||||||
|
? this.$capitalize(this.$p.t(phrase[0], phrase[1]))
|
||||||
|
: language;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<section
|
||||||
|
id="news-item-preview"
|
||||||
|
class="card bg-white shadow-sm border-0 mb-4 position-sticky"
|
||||||
|
style="top: 1rem"
|
||||||
|
aria-labelledby="news-item-preview-heading"
|
||||||
|
>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-between align-items-center gap-3 mb-3">
|
||||||
|
<h4 id="news-item-preview-heading" class="card-title fhc-primary-color mb-0">{{ $capitalize($p.t('ui', 'newsPreview')) }}</h4>
|
||||||
|
<form-input
|
||||||
|
v-if="availableLanguages.length > 1"
|
||||||
|
v-model="selectedLanguage"
|
||||||
|
type="select"
|
||||||
|
name="previewLanguage"
|
||||||
|
class="m-0"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="language in availableLanguages"
|
||||||
|
:key="language"
|
||||||
|
:value="language"
|
||||||
|
>
|
||||||
|
{{ getLanguageLabel(language) }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
</div>
|
||||||
|
<article v-if="translation" class="card border">
|
||||||
|
<header class="card-header" :class="translation.isPublished ? 'fhc-primary' : 'bg-secondary bg-opacity-25 text-secondary'">
|
||||||
|
<div class="d-flex justify-content-between align-items-start gap-3">
|
||||||
|
<h5 class="mb-0">{{ translation.title || $capitalize($p.t('ui', 'untitledNews')) }}</h5>
|
||||||
|
<span v-if="!translation.isPublished" class="badge text-bg-secondary">{{ $capitalize($p.t('ui', 'notPublishedYet')) }}</span>
|
||||||
|
</div>
|
||||||
|
<address v-if="translation.author" class="small mb-0 mt-2">{{ translation.author }}</address>
|
||||||
|
</header>
|
||||||
|
<div class="card-body">
|
||||||
|
<div v-if="parsedTranslationText" class="card-text" v-html="parsedTranslationText"></div>
|
||||||
|
<p v-else class="text-muted mb-0">{{ $capitalize($p.t('ui', 'newsContentPreviewHint')) }}</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<p v-else class="text-muted mb-0">{{ $capitalize($p.t('ui', 'selectLanguageTabToPreview')) }}</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
import Pagination from '../../../Pagination/Pagination.js';
|
||||||
|
import ApiNewsAdministration from '../../../../api/factory/newsAdministration.js';
|
||||||
|
import NewsListItem from './NewsListItem.js';
|
||||||
|
import NewsListFilter from './NewsListFilter.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewsList',
|
||||||
|
emits: ['edit'],
|
||||||
|
components: {
|
||||||
|
NewsListItem,
|
||||||
|
NewsListFilter,
|
||||||
|
Pagination,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
newsItems: [],
|
||||||
|
isLoading: false,
|
||||||
|
loadError: null,
|
||||||
|
maxPageCount: 0,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
isPublishedFilter: 'all',
|
||||||
|
isActiveFilter: true,
|
||||||
|
degreeProgramShortCodeFilter: null,
|
||||||
|
semesterFilter: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$p.user_language.value': function () {
|
||||||
|
this.page = 1;
|
||||||
|
this.fetchNews();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sprache() {
|
||||||
|
return this.$p.user_language.value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getNewsItem(newsId) {
|
||||||
|
await this.fetchNews();
|
||||||
|
|
||||||
|
return this.newsItems.find(
|
||||||
|
(news) => String(news.newsId) === String(newsId),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
async fetchNews() {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.loadError = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$api.call(
|
||||||
|
ApiNewsAdministration.getNews(
|
||||||
|
this.page,
|
||||||
|
this.pageSize,
|
||||||
|
this.sprache,
|
||||||
|
this.isPublishedFilter,
|
||||||
|
this.isActiveFilter,
|
||||||
|
this.degreeProgramShortCodeFilter,
|
||||||
|
this.semesterFilter,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
this.newsItems = this.parseNewsHtml(response.data);
|
||||||
|
this.maxPageCount = response.meta.row_count;
|
||||||
|
} catch (error) {
|
||||||
|
this.newsItems = [];
|
||||||
|
this.loadError = this.$capitalize(this.$p.t('ui', 'fehlerBeimLesen'));
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseNewsHtml(html) {
|
||||||
|
if (!html) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = new DOMParser().parseFromString(html, 'text/html');
|
||||||
|
|
||||||
|
return Array.from(document.querySelectorAll('.news-list-item')).map(
|
||||||
|
(article) => this.parseNewsItem(article),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
parseNewsItem(article) {
|
||||||
|
const newsId = article
|
||||||
|
.querySelector('[news-id]')
|
||||||
|
?.getAttribute('news-id');
|
||||||
|
const title = article.querySelector('.card-header h2');
|
||||||
|
const author = article.querySelector('address');
|
||||||
|
const time = article.querySelector('time');
|
||||||
|
const isPublished = article.querySelector(
|
||||||
|
'.card-header span[is-published]',
|
||||||
|
);
|
||||||
|
|
||||||
|
const isActive =
|
||||||
|
article
|
||||||
|
.querySelector('.card-header span[is-active]')
|
||||||
|
?.getAttribute('is-active') === 'true';
|
||||||
|
|
||||||
|
const content = article.querySelector('.card-text');
|
||||||
|
|
||||||
|
return {
|
||||||
|
newsId: newsId ? Number(newsId) : null,
|
||||||
|
language: this.sprache,
|
||||||
|
title: title ? title.textContent.trim() : '',
|
||||||
|
author: author ? author.textContent.trim() : '',
|
||||||
|
date: time ? time.textContent.trim() : '',
|
||||||
|
dateTime: time ? time.getAttribute('datetime') || '' : '',
|
||||||
|
content: content ? content.innerHTML : '',
|
||||||
|
isPublished: isPublished
|
||||||
|
? isPublished.getAttribute('is-published') === 'true'
|
||||||
|
: null,
|
||||||
|
isActive,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
afterPageUpdated(event) {
|
||||||
|
this.page = event.page;
|
||||||
|
this.pageSize = event.rows;
|
||||||
|
this.fetchNews();
|
||||||
|
this.$refs.newsListHeading.scrollIntoView({ block: 'end' });
|
||||||
|
},
|
||||||
|
afterFilterChanged(filters) {
|
||||||
|
this.isPublishedFilter = filters.isPublished;
|
||||||
|
this.isActiveFilter = filters.isActive;
|
||||||
|
this.degreeProgramShortCodeFilter = filters.degreeProgramShortCode;
|
||||||
|
this.semesterFilter = filters.semester;
|
||||||
|
this.page = 1;
|
||||||
|
this.fetchNews();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$p.loadCategory(['filter', 'global', 'lehre', 'ui']).then(() => {
|
||||||
|
this.phrasesLoaded = true;
|
||||||
|
});
|
||||||
|
this.fetchNews();
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<section class="mt-4" aria-labelledby="news-administration-list-heading">
|
||||||
|
<h3 id="news-administration-list-heading" ref="newsListHeading" class="fhc-primary-color">
|
||||||
|
{{ $capitalize($p.t('ui', 'news')) }}
|
||||||
|
</h3>
|
||||||
|
<hr>
|
||||||
|
<news-list-filter @filter-changed="afterFilterChanged"></news-list-filter>
|
||||||
|
<div v-if="isLoading" class="d-flex justify-content-center py-4" role="status">
|
||||||
|
<span class="spinner-border" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">{{ $capitalize($p.t('ui', 'loadingNews')) }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="loadError" class="alert alert-danger" role="alert">
|
||||||
|
{{ loadError }}
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!newsItems.length" class="alert alert-info" role="status">
|
||||||
|
{{ $capitalize($p.t('ui', 'noNewsAvailable')) }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="pt-3">
|
||||||
|
<news-list-item
|
||||||
|
v-for="(news, index) in newsItems"
|
||||||
|
:key="news.newsId || page + '-' + index"
|
||||||
|
:news="news"
|
||||||
|
@edit="$emit('edit', $event)"
|
||||||
|
@deleted="fetchNews()"
|
||||||
|
></news-list-item>
|
||||||
|
<pagination
|
||||||
|
v-if="maxPageCount > pageSize"
|
||||||
|
:page="page"
|
||||||
|
:page_size="pageSize"
|
||||||
|
:maxPageCount="maxPageCount"
|
||||||
|
@pageUpdated="afterPageUpdated"
|
||||||
|
></pagination>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
import FormInput from '../../../Form/Input.js';
|
||||||
|
import ApiStudiengang from '../../../../api/factory/studiengang.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewsListFilter',
|
||||||
|
emits: ['filter-changed'],
|
||||||
|
components: {
|
||||||
|
FormInput,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isPublished: 'all',
|
||||||
|
isActive: true,
|
||||||
|
degreeProgram: null,
|
||||||
|
semester: null,
|
||||||
|
degreePrograms: [],
|
||||||
|
filteredDegreePrograms: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
semesters() {
|
||||||
|
this.$p.user_language.value;
|
||||||
|
const semesterLabel = this.$capitalize(this.$p.t('lehre', 'semester'));
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$capitalize(this.$p.t('ui', 'all_semester')),
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
...Array.from({ length: 8 }, (_, index) => ({
|
||||||
|
label: `${index + 1}. ${semesterLabel}`,
|
||||||
|
value: String(index + 1),
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
dropdownParsedDegreePrograms() {
|
||||||
|
const degreePrograms = this.degreePrograms
|
||||||
|
.map((degreeProgram) => {
|
||||||
|
let labelFragment = degreeProgram.typ + degreeProgram.kurzbz;
|
||||||
|
labelFragment = labelFragment.trim().toUpperCase();
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: `${labelFragment} (${degreeProgram.bezeichnung})`,
|
||||||
|
value: degreeProgram.studiengang_kz,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$capitalize(this.$p.t('ui', 'dropdownEmptyOption')),
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
...degreePrograms,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
isPublished() {
|
||||||
|
this.emitFilterChanged();
|
||||||
|
},
|
||||||
|
isActive() {
|
||||||
|
this.emitFilterChanged();
|
||||||
|
},
|
||||||
|
degreeProgram() {
|
||||||
|
this.emitFilterChanged();
|
||||||
|
},
|
||||||
|
semester() {
|
||||||
|
this.emitFilterChanged();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
emitFilterChanged() {
|
||||||
|
this.$emit('filter-changed', {
|
||||||
|
isPublished: this.isPublished,
|
||||||
|
isActive: this.isActive,
|
||||||
|
degreeProgramShortCode: this.degreeProgram?.value ?? null,
|
||||||
|
semester: this.semester,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
filterDegreePrograms(event) {
|
||||||
|
const query = event.query.toLowerCase();
|
||||||
|
|
||||||
|
this.filteredDegreePrograms = this.dropdownParsedDegreePrograms.filter(
|
||||||
|
(degreeProgram) => degreeProgram.label.toLowerCase().includes(query),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
try {
|
||||||
|
const response = await this.$api.call(ApiStudiengang.getDegreePrograms());
|
||||||
|
if (response.meta.status === 'success') {
|
||||||
|
this.degreePrograms = response.data.sort((a, b) =>
|
||||||
|
a.bezeichnung.localeCompare(b.bezeichnung),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.degreePrograms = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="border rounded p-3 mt-2 d-flex flex-wrap align-items-center gap-3">
|
||||||
|
<form-input
|
||||||
|
v-model="isPublished"
|
||||||
|
type="select"
|
||||||
|
name="is-published-filter"
|
||||||
|
:label="$capitalize($p.t('ui', 'visible'))"
|
||||||
|
container-class="mb-0"
|
||||||
|
class="w-auto"
|
||||||
|
>
|
||||||
|
<option value="all">{{ $capitalize($p.t('global', 'alle')) }}</option>
|
||||||
|
<option :value="true">{{ $capitalize($p.t('ui', 'visible')) }}</option>
|
||||||
|
<option :value="false">{{ $capitalize($p.t('ui', 'notPublishedYet')) }}</option>
|
||||||
|
</form-input>
|
||||||
|
<form-input
|
||||||
|
v-model="degreeProgram"
|
||||||
|
:label="$capitalize($p.t('lehre', 'studiengang'))"
|
||||||
|
:suggestions="filteredDegreePrograms"
|
||||||
|
:optionValue="(option) => option.value"
|
||||||
|
:optionLabel="(option) => option.label"
|
||||||
|
@complete="filterDegreePrograms($event)"
|
||||||
|
type="autocomplete"
|
||||||
|
name="degree-program-filter"
|
||||||
|
dropdown
|
||||||
|
clear
|
||||||
|
></form-input>
|
||||||
|
<form-input
|
||||||
|
v-model="semester"
|
||||||
|
:label="$capitalize($p.t('lehre', 'studiensemester'))"
|
||||||
|
type="select"
|
||||||
|
name="semester-filter"
|
||||||
|
container-class="mb-0"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="semesterOption in semesters"
|
||||||
|
:key="semesterOption.value"
|
||||||
|
:value="semesterOption.value"
|
||||||
|
>
|
||||||
|
{{ semesterOption.label }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
<form-input
|
||||||
|
v-model="isActive"
|
||||||
|
type="checkbox"
|
||||||
|
name="is-active-filter"
|
||||||
|
:label="$capitalize($p.t('global', 'aktiv'))"
|
||||||
|
container-class="mb-0 align-self-end"
|
||||||
|
></form-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import ApiNewsAdministration from '../../../../api/factory/newsAdministration.js';
|
||||||
|
|
||||||
|
const createCollapseId = () => {
|
||||||
|
const randomNumber = Math.floor(
|
||||||
|
1000000000000000 + Math.random() * 8000000000000000,
|
||||||
|
);
|
||||||
|
return `news-administration-item-${randomNumber}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NewsListItem',
|
||||||
|
emits: ['edit', 'deleted'],
|
||||||
|
props: {
|
||||||
|
news: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isExpanded: false,
|
||||||
|
isDeleting: false,
|
||||||
|
collapseId: createCollapseId(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleContent() {
|
||||||
|
this.isExpanded = !this.isExpanded;
|
||||||
|
},
|
||||||
|
editNews(news) {
|
||||||
|
this.$emit('edit', news);
|
||||||
|
},
|
||||||
|
async deleteNews(news) {
|
||||||
|
if (this.isDeleting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(await this.$fhcAlert.confirm({
|
||||||
|
message: this.$capitalize(this.$p.t('ui', 'frageSicherLoeschen')),
|
||||||
|
acceptClass: 'btn btn-danger',
|
||||||
|
})) === false
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isDeleting = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$api.call(
|
||||||
|
ApiNewsAdministration.deleteNewsItem(news.newsId),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.meta.status !== 'success') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('deleted', news);
|
||||||
|
} catch (error) {
|
||||||
|
this.$fhcAlert.handleSystemError(error);
|
||||||
|
} finally {
|
||||||
|
this.isDeleting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$p.loadCategory(['global', 'ui']).then(() => {
|
||||||
|
this.phrasesLoaded = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<article
|
||||||
|
class="card mb-3"
|
||||||
|
:class="{'border-secondary': !news.isPublished || !news.isActive}"
|
||||||
|
>
|
||||||
|
<header
|
||||||
|
class="card-header d-flex justify-content-between align-items-center gap-3"
|
||||||
|
:class="{
|
||||||
|
'fhc-primary': news.isPublished && news.isActive,
|
||||||
|
'bg-secondary bg-opacity-25 text-secondary': !news.isPublished || !news.isActive,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-link text-reset text-decoration-none text-start d-flex align-items-center gap-2 flex-grow-1 p-0"
|
||||||
|
:aria-expanded="isExpanded"
|
||||||
|
:aria-controls="collapseId"
|
||||||
|
@click="toggleContent"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa-solid"
|
||||||
|
:class="isExpanded ? 'fa-chevron-down' : 'fa-chevron-right'"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="h5 mb-0">{{ news.title }}</span>
|
||||||
|
<span v-if="!news.isPublished" class="badge text-bg-secondary">
|
||||||
|
{{ $capitalize($p.t('ui', 'notPublishedYet')) }}
|
||||||
|
</span>
|
||||||
|
<span v-if="!news.isActive" class="badge text-bg-secondary">
|
||||||
|
{{ $capitalize($p.t('ui', 'noLongerActive')) }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div class="d-flex align-items-center gap-2 flex-shrink-0">
|
||||||
|
<div class="text-end">
|
||||||
|
<address v-if="news.author" class="fw-bold small mb-0">
|
||||||
|
{{ news.author }}
|
||||||
|
</address>
|
||||||
|
<time v-if="news.date" class="small" :datetime="news.dateTime">
|
||||||
|
{{ news.date }}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-1" :aria-label="$capitalize($p.t('global', 'actions'))">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm rounded-circle shadow-sm d-inline-flex align-items-center justify-content-center"
|
||||||
|
:class="news.isPublished ? 'btn-light text-primary' : 'btn-outline-secondary bg-white'"
|
||||||
|
style="width: 1.75rem; height: 1.75rem"
|
||||||
|
:title="$capitalize($p.t('ui', 'bearbeiten'))"
|
||||||
|
:aria-label="$capitalize($p.t('ui', 'bearbeiten'))"
|
||||||
|
@click="editNews(news)"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-pen" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm rounded-circle shadow-sm d-inline-flex align-items-center justify-content-center"
|
||||||
|
:class="news.isPublished ? 'btn-light text-danger' : 'btn-outline-danger bg-white'"
|
||||||
|
style="width: 1.75rem; height: 1.75rem"
|
||||||
|
:title="$capitalize($p.t('ui', 'loeschen'))"
|
||||||
|
:aria-label="$capitalize($p.t('ui', 'loeschen'))"
|
||||||
|
:disabled="isDeleting"
|
||||||
|
@click="deleteNews(news)"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div v-show="isExpanded" :id="collapseId" class="card-body">
|
||||||
|
<div class="card-text" v-html="news.content"></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import FhcCalendar from "../../Calendar/LvPlan.js";
|
import FhcCalendar from '../../Calendar/LvPlan.js';
|
||||||
|
|
||||||
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
||||||
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
||||||
@@ -9,10 +9,10 @@ export const DEFAULT_MODE_LVPLAN_DESKTOP = 'Week';
|
|||||||
export default {
|
export default {
|
||||||
name: 'LvPlan',
|
name: 'LvPlan',
|
||||||
components: {
|
components: {
|
||||||
FhcCalendar
|
FhcCalendar,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
propsViewData: Object
|
propsViewData: Object,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -24,65 +24,102 @@ export default {
|
|||||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: ["isMobile"],
|
inject: {
|
||||||
computed:{
|
isMobile: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
currentDay() {
|
currentDay() {
|
||||||
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(this.timezone).toISODate();
|
return (
|
||||||
|
this.propsViewData?.focus_date ||
|
||||||
|
luxon.DateTime.now().setZone(this.timezone).toISODate()
|
||||||
|
);
|
||||||
},
|
},
|
||||||
currentMode() {
|
currentMode() {
|
||||||
const defaultMode = this.isMobile ? DEFAULT_MODE_LVPLAN_MOBILE : DEFAULT_MODE_LVPLAN_DESKTOP;
|
const defaultMode = this.isMobile
|
||||||
|
? DEFAULT_MODE_LVPLAN_MOBILE
|
||||||
|
: DEFAULT_MODE_LVPLAN_DESKTOP;
|
||||||
return this.propsViewData?.mode || defaultMode;
|
return this.propsViewData?.mode || defaultMode;
|
||||||
},
|
},
|
||||||
downloadLinks() {
|
downloadLinks() {
|
||||||
if (!this.studiensemester_start || !this.studiensemester_ende || !this.uid)
|
if (
|
||||||
|
!this.studiensemester_start ||
|
||||||
|
!this.studiensemester_ende ||
|
||||||
|
!this.uid
|
||||||
|
)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const opts = { zone: this.timezone };
|
|
||||||
const start = luxon.DateTime
|
|
||||||
.fromISO(this.studiensemester_start, opts)
|
|
||||||
.toUnixInteger();
|
|
||||||
const ende = luxon.DateTime
|
|
||||||
.fromISO(this.studiensemester_ende, opts)
|
|
||||||
.toUnixInteger();
|
|
||||||
|
|
||||||
const download_link = FHC_JS_DATA_STORAGE_OBJECT.app_root
|
const opts = { zone: this.timezone };
|
||||||
+ 'cis/private/lvplan/stpl_kalender.php'
|
const start = luxon.DateTime.fromISO(
|
||||||
+ '?type=student'
|
this.studiensemester_start,
|
||||||
+ '&pers_uid=' + this.uid
|
opts,
|
||||||
+ '&begin=' + start
|
).toUnixInteger();
|
||||||
+ '&ende=' + ende;
|
const ende = luxon.DateTime.fromISO(
|
||||||
|
this.studiensemester_ende,
|
||||||
|
opts,
|
||||||
|
).toUnixInteger();
|
||||||
|
|
||||||
|
const download_link =
|
||||||
|
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||||
|
'cis/private/lvplan/stpl_kalender.php' +
|
||||||
|
'?type=student' +
|
||||||
|
'&pers_uid=' +
|
||||||
|
this.uid +
|
||||||
|
'&begin=' +
|
||||||
|
start +
|
||||||
|
'&ende=' +
|
||||||
|
ende;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ title: "excel", icon: 'fa-solid fa-file-excel', link: download_link + '&format=excel' },
|
{
|
||||||
{ title: "csv", icon: 'fa-solid fa-file-csv', link: download_link + '&format=csv' },
|
title: 'excel',
|
||||||
{ title: "ical1", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=1&target=ical' },
|
icon: 'fa-solid fa-file-excel',
|
||||||
{ title: "ical2", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=2&target=ical' }
|
link: download_link + '&format=excel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'csv',
|
||||||
|
icon: 'fa-solid fa-file-csv',
|
||||||
|
link: download_link + '&format=csv',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'ical1',
|
||||||
|
icon: 'fa-regular fa-calendar',
|
||||||
|
link: download_link + '&format=ical&version=1&target=ical',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'ical2',
|
||||||
|
icon: 'fa-regular fa-calendar',
|
||||||
|
link: download_link + '&format=ical&version=2&target=ical',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleChangeDate(day, newMode) {
|
handleChangeDate(day, newMode) {
|
||||||
return this.handleChangeMode(newMode, day);
|
return this.handleChangeMode(newMode, day);
|
||||||
},
|
},
|
||||||
handleChangeMode(newMode, day) {
|
handleChangeMode(newMode, day) {
|
||||||
const mode = newMode[0].toUpperCase() + newMode.slice(1)
|
const mode = newMode[0].toUpperCase() + newMode.slice(1);
|
||||||
const focus_date = day.toISODate();
|
const focus_date = day.toISODate();
|
||||||
|
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: "LvPlan",
|
name: 'LvPlan',
|
||||||
params: {
|
params: {
|
||||||
mode,
|
mode,
|
||||||
focus_date,
|
focus_date,
|
||||||
lv_id: this.propsViewData?.lv_id ?? null
|
lv_id: this.propsViewData?.lv_id ?? null,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateRange(rangeInterval) {
|
updateRange(rangeInterval) {
|
||||||
this.$api
|
this.$api
|
||||||
.call(ApiLvPlan.studiensemesterDateInterval(
|
.call(
|
||||||
rangeInterval.end.startOf('week').toISODate()
|
ApiLvPlan.studiensemesterDateInterval(
|
||||||
))
|
rangeInterval.end.startOf('week').toISODate(),
|
||||||
.then(res => {
|
),
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
this.studiensemester_kurzbz = res.data.studiensemester_kurzbz;
|
this.studiensemester_kurzbz = res.data.studiensemester_kurzbz;
|
||||||
this.studiensemester_start = res.data.start;
|
this.studiensemester_start = res.data.start;
|
||||||
this.studiensemester_ende = res.data.ende;
|
this.studiensemester_ende = res.data.ende;
|
||||||
@@ -90,19 +127,25 @@ export default {
|
|||||||
},
|
},
|
||||||
getPromiseFunc(start, end) {
|
getPromiseFunc(start, end) {
|
||||||
return [
|
return [
|
||||||
this.$api.call(ApiLvPlan.LvPlanEvents(start.toISODate(), end.toISODate(), this.propsViewData.lv_id)),
|
this.$api.call(
|
||||||
this.$api.call(ApiLvPlan.getLvPlanReservierungen(start.toISODate(), end.toISODate()))
|
ApiLvPlan.LvPlanEvents(
|
||||||
|
start.toISODate(),
|
||||||
|
end.toISODate(),
|
||||||
|
this.propsViewData.lv_id,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
this.$api.call(
|
||||||
|
ApiLvPlan.getLvPlanReservierungen(start.toISODate(), end.toISODate()),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.$api
|
this.$api.call(ApiAuthinfo.getAuthUID()).then((res) => {
|
||||||
.call(ApiAuthinfo.getAuthUID())
|
this.uid = res.data.uid;
|
||||||
.then(res => {
|
});
|
||||||
this.uid = res.data.uid;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
template: /*html*/`
|
template: /*html*/ `
|
||||||
<div class="cis-lvplan-personal d-flex flex-column h-100">
|
<div class="cis-lvplan-personal d-flex flex-column h-100">
|
||||||
<h2>
|
<h2>
|
||||||
{{ $p.t('lehre/stundenplan') }}
|
{{ $p.t('lehre/stundenplan') }}
|
||||||
@@ -144,5 +187,5 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fhc-calendar>
|
</fhc-calendar>
|
||||||
</div>`
|
</div>`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,72 +1,85 @@
|
|||||||
import CisMenuEntry from "./Menu/Entry.js";
|
import CisMenuEntry from './Menu/Entry.js';
|
||||||
import FhcSearchbar from "../searchbar/searchbar.js";
|
import FhcSearchbar from '../searchbar/searchbar.js';
|
||||||
import CisSprachen from "./Sprachen.js"
|
import CisSprachen from './Sprachen.js';
|
||||||
import ThemeSwitch from "./ThemeSwitch.js";
|
import ThemeSwitch from './ThemeSwitch.js';
|
||||||
import ApiCisMenu from '../../api/factory/cis/menu.js';
|
import ApiCisMenu from '../../api/factory/cis/menu.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
CisMenuEntry,
|
CisMenuEntry,
|
||||||
FhcSearchbar,
|
FhcSearchbar,
|
||||||
CisSprachen,
|
CisSprachen,
|
||||||
ThemeSwitch,
|
ThemeSwitch,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
rootUrl: String,
|
rootUrl: String,
|
||||||
logoUrl: String,
|
logoUrl: String,
|
||||||
avatarUrl: String,
|
avatarUrl: String,
|
||||||
logoutUrl: String,
|
logoutUrl: String,
|
||||||
selectedtypes: Array,
|
selectedtypes: Array,
|
||||||
searchbaroptions: Object,
|
searchbaroptions: Object,
|
||||||
searchfunction: Function
|
searchfunction: Function,
|
||||||
},
|
},
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
entries: [],
|
entries: [],
|
||||||
activeEntry:null,
|
activeEntry: null,
|
||||||
url:null,
|
url: null,
|
||||||
urlMatchRankings:[],
|
urlMatchRankings: [],
|
||||||
navUserDropdown:null,
|
navUserDropdown: null,
|
||||||
menuOpen:true,
|
menuOpen: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: ["isNarrow", "isMobile"],
|
inject: {
|
||||||
provide(){
|
isNarrow: {
|
||||||
return{
|
default: false,
|
||||||
|
},
|
||||||
|
isMobile: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
setActiveEntry: this.setActiveEntry,
|
setActiveEntry: this.setActiveEntry,
|
||||||
addUrlCount: this.addUrlCount,
|
addUrlCount: this.addUrlCount,
|
||||||
makeParentContentActive: this.makeParentContentActive,
|
makeParentContentActive: this.makeParentContentActive,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed:{
|
computed: {
|
||||||
menuCollapseAriaLabel(){
|
menuCollapseAriaLabel() {
|
||||||
if(this.menuOpen){
|
if (this.menuOpen) {
|
||||||
return this.$p.t('global', 'collapseMenu');
|
return this.$p.t('global', 'collapseMenu');
|
||||||
}else{
|
} else {
|
||||||
return this.$p.t('global', 'extendMenu');
|
return this.$p.t('global', 'extendMenu');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
highestMatchingUrlCount(){
|
highestMatchingUrlCount() {
|
||||||
// gets the hightest ranking inside the array
|
// gets the hightest ranking inside the array
|
||||||
let highestMatch = Math.max(...this.urlMatchRankings);
|
let highestMatch = Math.max(...this.urlMatchRankings);
|
||||||
|
|
||||||
if(this.urlMatchRankings.length > 0){
|
if (this.urlMatchRankings.length > 0) {
|
||||||
// if more than one entry has the same ranking, none should be active
|
// if more than one entry has the same ranking, none should be active
|
||||||
return this.urlMatchRankings.filter((value)=>value == highestMatch).length > 1 ? null : highestMatch;
|
return this.urlMatchRankings.filter((value) => value == highestMatch)
|
||||||
|
.length > 1
|
||||||
|
? null
|
||||||
|
: highestMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
site_url(){
|
site_url() {
|
||||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
return (
|
||||||
|
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||||
|
FHC_JS_DATA_STORAGE_OBJECT.ci_router
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchMenu() {
|
fetchMenu() {
|
||||||
return this.$api
|
return this.$api
|
||||||
.call(ApiCisMenu.getMenu())
|
.call(ApiCisMenu.getMenu())
|
||||||
.then(res => res.data)
|
.then((res) => res.data)
|
||||||
.then(menu => {
|
.then((menu) => {
|
||||||
this.entries = menu;
|
this.entries = menu;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -76,43 +89,54 @@ export default {
|
|||||||
this.navUserDropdown.hide();
|
this.navUserDropdown.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleShowNavUser(){
|
handleShowNavUser() {
|
||||||
document.addEventListener("click", this.checkSettingsVisibility);
|
document.addEventListener('click', this.checkSettingsVisibility);
|
||||||
},
|
},
|
||||||
handleHideNavUser(){
|
handleHideNavUser() {
|
||||||
document.removeEventListener("click", this.checkSettingsVisibility);
|
document.removeEventListener('click', this.checkSettingsVisibility);
|
||||||
},
|
},
|
||||||
makeParentContentActive(content_id, collection=this.entries, parent=null){
|
makeParentContentActive(
|
||||||
if(!collection) return;
|
content_id,
|
||||||
if (typeof collection == 'object' && !Array.isArray(collection) && Object.entries(collection).length > 0) {
|
collection = this.entries,
|
||||||
|
parent = null,
|
||||||
|
) {
|
||||||
|
if (!collection) return;
|
||||||
|
if (
|
||||||
|
typeof collection == 'object' &&
|
||||||
|
!Array.isArray(collection) &&
|
||||||
|
Object.entries(collection).length > 0
|
||||||
|
) {
|
||||||
collection = Object.values(collection);
|
collection = Object.values(collection);
|
||||||
}
|
}
|
||||||
for(let entry of collection){
|
for (let entry of collection) {
|
||||||
if(entry.content_id == content_id){
|
if (entry.content_id == content_id) {
|
||||||
this.activeEntry = parent;
|
this.activeEntry = parent;
|
||||||
}
|
}
|
||||||
this.makeParentContentActive(content_id, entry.childs, entry.content_id);
|
this.makeParentContentActive(
|
||||||
|
content_id,
|
||||||
|
entry.childs,
|
||||||
|
entry.content_id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
addUrlCount(count){
|
addUrlCount(count) {
|
||||||
this.urlMatchRankings.push(count);
|
this.urlMatchRankings.push(count);
|
||||||
},
|
},
|
||||||
|
|
||||||
setActiveEntry(content_id){
|
setActiveEntry(content_id) {
|
||||||
this.activeEntry = content_id;
|
this.activeEntry = content_id;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created(){
|
created() {
|
||||||
this.fetchMenu();
|
this.fetchMenu();
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted() {
|
||||||
this.$p.loadCategory(['ui', 'global', 'profilUpdate'])
|
this.$p.loadCategory(['ui', 'global', 'profilUpdate']);
|
||||||
this.navUserDropdown = new bootstrap.Collapse(this.$refs.navUserDropdown,{
|
this.navUserDropdown = new bootstrap.Collapse(this.$refs.navUserDropdown, {
|
||||||
toggle: false
|
toggle: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
template: /*html*/`
|
template: /*html*/ `
|
||||||
<div id="cis-header-bar" class="d-flex flex-row flex-grow-1">
|
<div id="cis-header-bar" class="d-flex flex-row flex-grow-1">
|
||||||
<div id="nav-logo" class="d-none d-lg-block">
|
<div id="nav-logo" class="d-none d-lg-block">
|
||||||
<div class="d-flex h-100 justify-content-between">
|
<div class="d-flex h-100 justify-content-between">
|
||||||
@@ -212,5 +236,5 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>`
|
</nav>`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,18 +6,17 @@ import ApiCms from '../../api/factory/cms.js';
|
|||||||
const MAX_LOADED_NEWS = 30;
|
const MAX_LOADED_NEWS = 30;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "WidgetsNews",
|
name: 'WidgetsNews',
|
||||||
components: {
|
components: {
|
||||||
BsModal
|
BsModal,
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
allNewsList: [],
|
allNewsList: [],
|
||||||
singleNews: {},
|
singleNews: {},
|
||||||
selected: null,
|
selected: null,
|
||||||
size:0,
|
size: 0,
|
||||||
}),
|
}),
|
||||||
props: ['width', 'height']
|
props: ['width', 'height'],
|
||||||
,
|
|
||||||
mixins: [AbstractWidget],
|
mixins: [AbstractWidget],
|
||||||
computed: {
|
computed: {
|
||||||
sizeClass() {
|
sizeClass() {
|
||||||
@@ -28,7 +27,6 @@ export default {
|
|||||||
// let quantity = this.width;
|
// let quantity = this.width;
|
||||||
let quantity = MAX_LOADED_NEWS;
|
let quantity = MAX_LOADED_NEWS;
|
||||||
|
|
||||||
|
|
||||||
if (this.width === 1) {
|
if (this.width === 1) {
|
||||||
quantity = this.height === 1 ? 4 : MAX_LOADED_NEWS;
|
quantity = this.height === 1 ? 4 : MAX_LOADED_NEWS;
|
||||||
}
|
}
|
||||||
@@ -39,107 +37,129 @@ export default {
|
|||||||
},
|
},
|
||||||
carouselItems() {
|
carouselItems() {
|
||||||
return this.allNewsList.reduce((acc, cur) => {
|
return this.allNewsList.reduce((acc, cur) => {
|
||||||
const el = document.getElementById('card-'+cur.news_id)
|
const el = document.getElementById('card-' + cur.news_id);
|
||||||
acc.push(el);
|
acc.push(el);
|
||||||
return acc
|
return acc;
|
||||||
}, [])
|
}, []);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateNewsContentClasses:function(){
|
updateNewsContentClasses: function () {
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
document.querySelectorAll(".fhc-news-card-item .card-body, .fhc-news-card-item .card, .fhc-news-card-item .card-header").forEach((el) => {
|
document
|
||||||
if (!el.classList.contains("border-0")) {
|
.querySelectorAll(
|
||||||
el.classList.add("border-0");
|
'.fhc-news-card-item .card-body, .fhc-news-card-item .card, .fhc-news-card-item .card-header',
|
||||||
}
|
)
|
||||||
});
|
.forEach((el) => {
|
||||||
document.querySelectorAll(".fhc-news-card-item .card-header").forEach((el) => {
|
if (!el.classList.contains('border-0')) {
|
||||||
if (!el.classList.contains("px-5")) {
|
el.classList.add('border-0');
|
||||||
el.classList.add("px-5");
|
}
|
||||||
}
|
});
|
||||||
if (!el.classList.contains("fhc-primary")) {
|
document
|
||||||
el.classList.add("fhc-primary");
|
.querySelectorAll('.fhc-news-card-item .card-header')
|
||||||
}
|
.forEach((el) => {
|
||||||
if (!el.classList.contains("position-sticky")) {
|
if (!el.classList.contains('px-5')) {
|
||||||
el.classList.add("position-sticky");
|
el.classList.add('px-5');
|
||||||
}
|
}
|
||||||
if (!el.classList.contains("top-0")) {
|
if (!el.classList.contains('fhc-primary')) {
|
||||||
el.classList.add("top-0");
|
el.classList.add('fhc-primary');
|
||||||
}
|
}
|
||||||
|
if (!el.classList.contains('position-sticky')) {
|
||||||
|
el.classList.add('position-sticky');
|
||||||
});
|
}
|
||||||
document.querySelectorAll(".fhc-news-card-item .card-header .row").forEach((el) => {
|
if (!el.classList.contains('top-0')) {
|
||||||
if (!el.classList.contains("w-100")) {
|
el.classList.add('top-0');
|
||||||
el.classList.add("w-100");
|
}
|
||||||
}
|
});
|
||||||
if (!el.classList.contains("align-items-center")) {
|
document
|
||||||
el.classList.add("align-items-center");
|
.querySelectorAll('.fhc-news-card-item .card-header .row')
|
||||||
}
|
.forEach((el) => {
|
||||||
});
|
if (!el.classList.contains('w-100')) {
|
||||||
document.querySelectorAll(".fhc-news-card-item .card-header .row h2").forEach((el) => {
|
el.classList.add('w-100');
|
||||||
if (!el.classList.contains("mb-0")) {
|
}
|
||||||
el.classList.add("mb-0");
|
if (!el.classList.contains('align-items-center')) {
|
||||||
}
|
el.classList.add('align-items-center');
|
||||||
});
|
}
|
||||||
})
|
});
|
||||||
|
document
|
||||||
|
.querySelectorAll('.fhc-news-card-item .card-header .row h2')
|
||||||
|
.forEach((el) => {
|
||||||
|
if (!el.classList.contains('mb-0')) {
|
||||||
|
el.classList.add('mb-0');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
formatDate: function (dateTime) {
|
formatDate: function (dateTime) {
|
||||||
const dt = new Date(dateTime);
|
const dt = new Date(dateTime);
|
||||||
return numberPadding(dt.getDate()) + '.' + numberPadding((dt.getMonth() + 1)) + '.' + dt.getFullYear();
|
return (
|
||||||
|
numberPadding(dt.getDate()) +
|
||||||
|
'.' +
|
||||||
|
numberPadding(dt.getMonth() + 1) +
|
||||||
|
'.' +
|
||||||
|
dt.getFullYear()
|
||||||
|
);
|
||||||
},
|
},
|
||||||
formatTime: function (dateTime) {
|
formatTime: function (dateTime) {
|
||||||
const dt = new Date(dateTime);
|
const dt = new Date(dateTime);
|
||||||
return numberPadding(dt.getHours()) + ':' + numberPadding(dt.getMinutes());
|
return (
|
||||||
|
numberPadding(dt.getHours()) + ':' + numberPadding(dt.getMinutes())
|
||||||
|
);
|
||||||
},
|
},
|
||||||
isString(value){
|
isString(value) {
|
||||||
return Object.prototype.toString.call(value) === '[object String]';
|
return Object.prototype.toString.call(value) === '[object String]';
|
||||||
},
|
},
|
||||||
setNext(){
|
setNext() {
|
||||||
const thisIndex = this.allNewsList.findIndex(n=>n.news_id == this.selected.news_id)
|
const thisIndex = this.allNewsList.findIndex(
|
||||||
const nextIndex = thisIndex == (this.allNewsList.length - 1) ? 0 : thisIndex + 1
|
(n) => n.news_id == this.selected.news_id,
|
||||||
|
);
|
||||||
|
const nextIndex =
|
||||||
|
thisIndex == this.allNewsList.length - 1 ? 0 : thisIndex + 1;
|
||||||
|
|
||||||
this.setSelected(this.allNewsList[nextIndex])
|
this.setSelected(this.allNewsList[nextIndex]);
|
||||||
this.updateNewsContentClasses();
|
this.updateNewsContentClasses();
|
||||||
|
|
||||||
},
|
},
|
||||||
setPrev() {
|
setPrev() {
|
||||||
const thisIndex = this.allNewsList.findIndex(n=>n.news_id == this.selected.news_id)
|
const thisIndex = this.allNewsList.findIndex(
|
||||||
const prevIndex = thisIndex ? thisIndex - 1 : this.allNewsList.length - 1
|
(n) => n.news_id == this.selected.news_id,
|
||||||
|
);
|
||||||
|
const prevIndex = thisIndex ? thisIndex - 1 : this.allNewsList.length - 1;
|
||||||
|
|
||||||
this.setSelected(this.allNewsList[prevIndex])
|
this.setSelected(this.allNewsList[prevIndex]);
|
||||||
this.updateNewsContentClasses();
|
this.updateNewsContentClasses();
|
||||||
},
|
},
|
||||||
getMenuItemClass(news) {
|
getMenuItemClass(news) {
|
||||||
let classString = ''
|
let classString = '';
|
||||||
if(this.selected && this.selected.news_id === news.news_id) {
|
if (this.selected && this.selected.news_id === news.news_id) {
|
||||||
classString += 'selected'
|
classString += 'selected';
|
||||||
}
|
}
|
||||||
return classString
|
return classString;
|
||||||
},
|
},
|
||||||
async setSelected(news) {
|
async setSelected(news) {
|
||||||
const clickedElement = document.getElementById('card-'+news.news_id);
|
const clickedElement = document.getElementById('card-' + news.news_id);
|
||||||
const clickedElementIndex = this.allNewsList.indexOf(news);
|
const clickedElementIndex = this.allNewsList.indexOf(news);
|
||||||
const oldElementIndex = this.allNewsList.indexOf(this.selected);
|
const oldElementIndex = this.allNewsList.indexOf(this.selected);
|
||||||
|
|
||||||
//if the clicked element is already active, do nothing
|
//if the clicked element is already active, do nothing
|
||||||
if(clickedElementIndex === oldElementIndex) return;
|
if (clickedElementIndex === oldElementIndex) return;
|
||||||
//add prev/next class to the clicked element
|
//add prev/next class to the clicked element
|
||||||
if(clickedElementIndex > oldElementIndex) {
|
if (clickedElementIndex > oldElementIndex) {
|
||||||
clickedElement.classList.add('carousel-item-next');
|
clickedElement.classList.add('carousel-item-next');
|
||||||
}else{
|
} else {
|
||||||
clickedElement.classList.add('carousel-item-prev');
|
clickedElement.classList.add('carousel-item-prev');
|
||||||
}
|
}
|
||||||
|
|
||||||
// move to clicked element
|
// move to clicked element
|
||||||
await Vue.nextTick(() => { this.carouselInstance.to(clickedElementIndex); })
|
await Vue.nextTick(() => {
|
||||||
|
this.carouselInstance.to(clickedElementIndex);
|
||||||
|
});
|
||||||
this.selected = news;
|
this.selected = news;
|
||||||
},
|
},
|
||||||
contentURI: function (content_id) {
|
contentURI: function (content_id) {
|
||||||
return (
|
return (
|
||||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||||
"/CisVue/Cms/content/" +
|
'/CisVue/Cms/content/' +
|
||||||
content_id
|
content_id
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -147,7 +167,7 @@ export default {
|
|||||||
return (
|
return (
|
||||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||||
"/CisVue/Cms/news"
|
'/CisVue/Cms/news'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
setSingleNews(singleNews) {
|
setSingleNews(singleNews) {
|
||||||
@@ -155,73 +175,72 @@ export default {
|
|||||||
this.$refs.newsModal.show();
|
this.$refs.newsModal.show();
|
||||||
},
|
},
|
||||||
initCarouselInstance() {
|
initCarouselInstance() {
|
||||||
Vue.nextTick(()=> {
|
Vue.nextTick(() => {
|
||||||
if(this.$refs.carousel) { // carousel ref might not exist in every widget width/height
|
if (this.$refs.carousel) {
|
||||||
|
// carousel ref might not exist in every widget width/height
|
||||||
this.carouselInstance = new bootstrap.Carousel(this.$refs.carousel, {
|
this.carouselInstance = new bootstrap.Carousel(this.$refs.carousel, {
|
||||||
wrap: false, // keep this off even though it actually wraps
|
wrap: false, // keep this off even though it actually wraps
|
||||||
interval: false
|
interval: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
initActiveItem() {
|
initActiveItem() {
|
||||||
Vue.nextTick(()=> {
|
Vue.nextTick(() => {
|
||||||
if (Array.isArray(this.$refs.carouselItems) && this.$refs.carouselItems.length >0) {
|
if (
|
||||||
this.$refs.carouselItems[0].classList.add("active")
|
Array.isArray(this.$refs.carouselItems) &&
|
||||||
|
this.$refs.carouselItems.length > 0
|
||||||
|
) {
|
||||||
|
this.$refs.carouselItems[0].classList.add('active');
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
width(newVal, oldVal) {
|
width(newVal, oldVal) {
|
||||||
if(oldVal == 1 && newVal > 1) { // carousel instance will have been disposed
|
if (oldVal == 1 && newVal > 1) {
|
||||||
this.initCarouselInstance()
|
// carousel instance will have been disposed
|
||||||
this.initActiveItem()
|
this.initCarouselInstance();
|
||||||
|
this.initActiveItem();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.$emit("setConfig", false);
|
this.$emit('setConfig', false);
|
||||||
this.$api
|
this.$api
|
||||||
.call(ApiCms.news(MAX_LOADED_NEWS))
|
.call(ApiCms.newsForWidget(MAX_LOADED_NEWS))
|
||||||
.then(res => res.data)
|
.then((res) => res.data)
|
||||||
.then((news) => {
|
.then((news) => {
|
||||||
this.allNewsList = Array.from(Object.values(news));
|
this.allNewsList = Array.from(Object.values(news));
|
||||||
this.allNewsList.sort((a, b) => {
|
this.allNewsList.sort((a, b) => {
|
||||||
return new Date(b.datum) - new Date(a.datum);
|
return new Date(b.datum) - new Date(a.datum);
|
||||||
});
|
});
|
||||||
this.selected = this.allNewsList.length ? this.allNewsList[0] : null
|
this.selected = this.allNewsList.length ? this.allNewsList[0] : null;
|
||||||
this.initActiveItem()
|
this.initActiveItem();
|
||||||
|
|
||||||
this.updateNewsContentClasses();
|
this.updateNewsContentClasses();
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("ERROR: ", err);
|
console.error('ERROR: ', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.$refs.container) {
|
if (this.$refs.container) {
|
||||||
new ResizeObserver(entries => {
|
new ResizeObserver((entries) => {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
let w = entry.contentBoxSize ? entry.contentBoxSize[0].inlineSize : entry.contentRect.width;
|
let w = entry.contentBoxSize
|
||||||
|
? entry.contentBoxSize[0].inlineSize
|
||||||
|
: entry.contentRect.width;
|
||||||
// TODO(chris): rework sizing
|
// TODO(chris): rework sizing
|
||||||
if (w > 600)
|
if (w > 600) this.size = 3;
|
||||||
this.size = 3;
|
else if (w > 350) this.size = 2;
|
||||||
else if (w > 350)
|
else if (w > 250) this.size = 1;
|
||||||
this.size = 2;
|
else this.size = 0;
|
||||||
else if (w > 250)
|
}
|
||||||
this.size = 1;
|
|
||||||
else
|
|
||||||
this.size = 0;
|
|
||||||
}
|
|
||||||
}).observe(this.$refs.container);
|
}).observe(this.$refs.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initCarouselInstance()
|
|
||||||
|
|
||||||
|
this.initCarouselInstance();
|
||||||
},
|
},
|
||||||
template: /*html*/ `
|
template: /*html*/ `
|
||||||
<div ref="container" class="widgets-news h-100" :class="sizeClass" >
|
<div ref="container" class="widgets-news h-100" :class="sizeClass" >
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export default {
|
|||||||
emits: [
|
emits: [
|
||||||
'update:modelValue',
|
'update:modelValue',
|
||||||
'change',
|
'change',
|
||||||
'changed'
|
'changed',
|
||||||
|
'tab-action'
|
||||||
],
|
],
|
||||||
props: {
|
props: {
|
||||||
config: {
|
config: {
|
||||||
@@ -234,6 +235,7 @@ export default {
|
|||||||
v-model="value"
|
v-model="value"
|
||||||
:config="tab.config"
|
:config="tab.config"
|
||||||
@update:suffix="updateSuffix($event)"
|
@update:suffix="updateSuffix($event)"
|
||||||
|
@tab-action="$emit('tab-action', { key: tab.key, payload: $event })"
|
||||||
></component>
|
></component>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</tabpanel>
|
</tabpanel>
|
||||||
@@ -265,6 +267,7 @@ export default {
|
|||||||
v-model="value"
|
v-model="value"
|
||||||
:config="currentTab.config"
|
:config="currentTab.config"
|
||||||
@update:suffix="updateSuffix($event)"
|
@update:suffix="updateSuffix($event)"
|
||||||
|
@tab-action="$emit('tab-action', { key: currentTab.key, payload: $event })"
|
||||||
></component>
|
></component>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,4 +275,3 @@ export default {
|
|||||||
|
|
||||||
</template>`
|
</template>`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+823
-1
@@ -25,7 +25,6 @@
|
|||||||
//flag for at least one new phrase
|
//flag for at least one new phrase
|
||||||
$new = false;
|
$new = false;
|
||||||
|
|
||||||
|
|
||||||
$phrases = array(
|
$phrases = array(
|
||||||
//******************* CORE/global
|
//******************* CORE/global
|
||||||
array(
|
array(
|
||||||
@@ -60359,10 +60358,833 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'visible',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'sichtbar',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'visible',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'publish',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'veroeffentlichen',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'publish',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// NEWS ADMINISTRATION UI PHRASES ----------------------------------------------
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'newsAdministration',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'News-Verwaltung',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'News Administration',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'createNews',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'News erstellen',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Create news',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'news',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'News',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'News',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'loadingNews',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'News werden geladen',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Loading news',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'noNewsAvailable',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Keine News verfügbar.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'No news available.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'addTranslationLanguage',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Übersetzungssprache hinzufügen',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Add translation language',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'allLanguagesAdded',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Alle verfügbaren Sprachen wurden hinzugefügt.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'All available languages have been added.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'selectLanguageForTranslation',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Wählen Sie eine Sprache aus, um eine Übersetzung hinzuzufügen.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Select a language to add a translation.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'removeLanguage',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Sprache entfernen',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Remove language',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'copyTranslationContent',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Übersetzungsinhalt kopieren',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Copy translation content',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'overwriteTranslationContent',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Übersetzungsinhalt überschreiben?',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Overwrite translation content?',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'copyingTranslation',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Das Kopieren von {source} ersetzt Verfasser*in, Titel und Text in {target}.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Copying from {source} will replace the author, title, and text in {target}.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'existingContentWillBeLost',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Der bestehende Inhalt der aktuellen Sprache geht verloren.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'The existing content in the current language will be lost.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'chooseAnotherLanguage',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Andere Sprache auswählen',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Choose another language',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'overwriteAndCopy',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Überschreiben und kopieren',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Overwrite and copy',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'chooseLanguageToCopyContent',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Wählen Sie die Sprache aus, deren Inhalt in {target} kopiert werden soll.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Choose the language whose content should be copied into {target}.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'addLanguageBeforeCopying',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Fügen Sie vor dem Kopieren eine weitere Sprache hinzu.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Add another language before copying content.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'newsPreview',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'News-Vorschau',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'News preview',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'untitledNews',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'News ohne Titel',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Untitled news',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'notPublishedYet',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Noch nicht veröffentlicht',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Not published yet',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'noLongerActive',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Nicht mehr aktiv',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'No longer active',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'newsContentPreviewHint',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Text eingeben, um die News-Inhaltsvorschau zu sehen.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Enter text to see the news content preview.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'selectLanguageTabToPreview',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Wählen Sie einen Sprachreiter aus, um dessen Inhalt in der Vorschau zu sehen.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Select a language tab to preview its content.',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'newsForm',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'News-Formular',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'News form',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'contentFormTitle',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => '{language} Inhaltsformular',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => '{language} Content form',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'showPreview',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Vorschau anzeigen',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Show preview',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'hidePreview',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Vorschau ausblenden',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Hide preview',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'copyContentFromAnotherLanguage',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Inhalt aus einer anderen Sprache kopieren',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Copy content from another language',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'franzoesisch',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Französisch',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'French',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'spanisch',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Spanisch',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Spanish',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'blocks',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Blöcke',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Blocks',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'paragraph',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Absatz',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Paragraph',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'heading1',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Überschrift 1',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Heading 1',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'heading2',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Überschrift 2',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Heading 2',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'heading3',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Überschrift 3',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Heading 3',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'heading4',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Überschrift 4',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Heading 4',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'heading5',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Überschrift 5',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Heading 5',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'heading6',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Überschrift 6',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Heading 6',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'ui',
|
||||||
|
'phrase' => 'dropdownEmptyOption',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => '--keine Auswahl--',
|
||||||
|
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => '--no selection--',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
// generic tabulator phrases END
|
// generic tabulator phrases END
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//***** CHECK PHRASES & PHRASENTEXTE in German and English.
|
//***** CHECK PHRASES & PHRASENTEXTE in German and English.
|
||||||
//***** INSERT into phrase_tbl if new app + category + phrase found in phrasen-array.
|
//***** INSERT into phrase_tbl if new app + category + phrase found in phrasen-array.
|
||||||
//***** INSERT into phrasentext_tbl if new text found in phrasen-phrases-array, conciders every language apart.
|
//***** INSERT into phrasentext_tbl if new text found in phrasen-phrases-array, conciders every language apart.
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
/cypress/screenshots
|
||||||
|
/cypress/videos
|
||||||
|
/results
|
||||||
Reference in New Issue
Block a user