mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-21 00:42:15 +00:00
Merge branch 'feature/76688-general-tabulator-issues_76776-localization' into cis40_2026-02_rc_abgabetool_finetuning_76688
This commit is contained in:
@@ -32,6 +32,8 @@ class Phrasen extends FHCAPI_Controller
|
||||
'setLanguage' => self::PERM_ANONYMOUS,
|
||||
'getLanguage' => self::PERM_ANONYMOUS,
|
||||
'getAllLanguages' => self::PERM_ANONYMOUS,
|
||||
'getPhrases' => self::PERM_ANONYMOUS,
|
||||
'getTabulatorPhrases' => self::PERM_ANONYMOUS,
|
||||
]);
|
||||
|
||||
$this->load->helper('hlp_language');
|
||||
@@ -92,4 +94,59 @@ class Phrasen extends FHCAPI_Controller
|
||||
$this->terminateWithSuccess($langs);
|
||||
}
|
||||
|
||||
public function getPhrases()
|
||||
{
|
||||
$postParams = $this->getPostJSON();
|
||||
|
||||
$languages = $postParams->languages;
|
||||
if (!$languages || !count($languages)) {
|
||||
$this->load->model('system/Sprache_model', 'sprachenModel');
|
||||
$activeLanguages = $this->sprachenModel->loadWhere(array('content' => true));
|
||||
$activeLanguagesData = $this->getDataOrTerminateWithError($activeLanguages);
|
||||
$languages = array_map(
|
||||
function ($languageData) {
|
||||
return $languageData->sprache;
|
||||
},
|
||||
$activeLanguagesData
|
||||
);
|
||||
}
|
||||
|
||||
$this->load->model('system/Phrase_model', 'phraseModel');
|
||||
$phrasesGroupedByCategory = $postParams->phrasesGroupedByCategory;
|
||||
$result = [];
|
||||
foreach ($languages as $language) {
|
||||
$phrases = $this->phraseModel->getPhrasesByCategoryAndPhrasesAndLanguage($phrasesGroupedByCategory, $language);
|
||||
$result[$language] = $this->getDataOrTerminateWithError($phrases);
|
||||
}
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
public function getTabulatorPhrases()
|
||||
{
|
||||
$languages = json_decode($this->input->get('languages'));
|
||||
if (!$languages || !count($languages)) {
|
||||
$this->load->model('system/Sprache_model', 'sprachenModel');
|
||||
$activeLanguages = $this->sprachenModel->loadWhere(array('content' => true));
|
||||
$activeLanguagesData = $this->getDataOrTerminateWithError($activeLanguages);
|
||||
$languages = array_map(
|
||||
function ($languageData) {
|
||||
return $languageData->sprache;
|
||||
},
|
||||
$activeLanguagesData
|
||||
);
|
||||
}
|
||||
|
||||
$this->load->model('system/Phrase_model', 'phraseModel');
|
||||
$result = [];
|
||||
foreach ($languages as $language) {
|
||||
$tabulatorPhrases = $this->phraseModel->getPhrasesByCategoryAndLanguage(['tabulator'], $language);
|
||||
$result[$language] = $this->getDataOrTerminateWithError($tabulatorPhrases);
|
||||
}
|
||||
|
||||
header('Pragma: private');
|
||||
header('Cache-Control: private, max-age=' . (60 * 60 * 24 * 30));
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,5 +41,24 @@ export default {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/phrasen/getAllLanguages'
|
||||
};
|
||||
}
|
||||
},
|
||||
getPhrases(phrasesGroupedByCategory, languages = []) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: '/api/frontend/v1/phrasen/getPhrases',
|
||||
params: {
|
||||
phrasesGroupedByCategory,
|
||||
languages,
|
||||
},
|
||||
};
|
||||
},
|
||||
getTabulatorPhrases(languages = []) {
|
||||
return {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/phrasen/getTabulatorPhrases',
|
||||
params: {
|
||||
languages: JSON.stringify(languages),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -36,7 +36,6 @@ export default {
|
||||
return {
|
||||
showModal: false,
|
||||
editDataFilter: null,
|
||||
arePhrasesPreloaded: false,
|
||||
// tabulator options
|
||||
funktionen_table_options: {
|
||||
persistenceID: "filterTableMaProfilFunktionen",
|
||||
@@ -49,6 +48,7 @@ export default {
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
responsiveLayoutCollapseStartOpen: false,
|
||||
locale: true,
|
||||
columns: [
|
||||
{
|
||||
title:
|
||||
@@ -63,7 +63,8 @@ export default {
|
||||
responsive: 0,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('ui/bezeichnung')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "ui/bezeichnung",
|
||||
field: "Bezeichnung",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
@@ -71,7 +72,8 @@ export default {
|
||||
responsive: 0,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('lehre/organisationseinheit')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "lehre/organisationseinheit",
|
||||
field: "Organisationseinheit",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
@@ -79,7 +81,8 @@ export default {
|
||||
responsive: 1,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('global/gueltigVon')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "global/gueltigVon",
|
||||
field: "Gültig_von",
|
||||
headerFilterFunc: 'dates',
|
||||
headerFilter: dateFilter,
|
||||
@@ -91,7 +94,8 @@ export default {
|
||||
responsive: 4,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('global/gueltigBis')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "global/gueltigBis",
|
||||
field: "Gültig_bis",
|
||||
headerFilterFunc: 'dates',
|
||||
headerFilter: dateFilter,
|
||||
@@ -103,7 +107,8 @@ export default {
|
||||
responsive: 3,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('profil/wochenstunden')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/wochenstunden",
|
||||
field: "Wochenstunden",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
@@ -124,6 +129,7 @@ export default {
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
data: [{betriebsmittel: "", Nummer: "", Ausgegeben_am: ""}],
|
||||
responsiveLayoutCollapseStartOpen: false,
|
||||
locale: true,
|
||||
columns: [
|
||||
{
|
||||
title:
|
||||
@@ -138,7 +144,8 @@ export default {
|
||||
responsive: 0,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('profil/entlehnteBetriebsmittel')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/entlehnteBetriebsmittel",
|
||||
field: "betriebsmittel",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
@@ -146,7 +153,8 @@ export default {
|
||||
responsive: 0,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('profil/inventarnummer')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/inventarnummer",
|
||||
field: "Nummer",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
@@ -155,7 +163,8 @@ export default {
|
||||
responsive: 2,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('profil/ausgabedatum')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/ausgabedatum",
|
||||
field: "Ausgegeben_am",
|
||||
headerFilterFunc: 'dates',
|
||||
headerFilter: dateFilter,
|
||||
@@ -230,10 +239,6 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
setTableColumnTitles() { // reevaluates computed phrasen
|
||||
if(this.$refs.betriebsmittelTable) this.$refs.betriebsmittelTable.tabulator.setColumns(this.betriebsmittel_table_options.columns);
|
||||
if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setColumns(this.funktionen_table_options.columns);
|
||||
},
|
||||
datetimeFormatterParams: function() {
|
||||
const params = {
|
||||
inputFormat:"yyyy-MM-dd",
|
||||
@@ -316,10 +321,6 @@ export default {
|
||||
},
|
||||
|
||||
created() {
|
||||
// preload phrasen
|
||||
this.$p.loadCategory(["ui","lehre","global","profil"]).then(() => {
|
||||
this.arePhrasesPreloaded = true;
|
||||
});
|
||||
//? sorts the profil Updates: pending -> accepted -> rejected
|
||||
this.data.profilUpdates?.sort(this.sortProfilUpdates);
|
||||
},
|
||||
@@ -330,9 +331,6 @@ export default {
|
||||
'data.mittel'(newVal) {
|
||||
if(this.$refs.betriebsmittelTable) this.$refs.betriebsmittelTable.tabulator.setData(newVal);
|
||||
},
|
||||
'language.value'(newVal) {
|
||||
this.setTableColumnTitles()
|
||||
}
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="container-fluid text-break fhc-form" >
|
||||
@@ -454,7 +452,6 @@ export default {
|
||||
<!-- FUNKTIONEN TABELLE -->
|
||||
<div class="col-12 mb-4" >
|
||||
<core-filter-cmpt
|
||||
v-if="arePhrasesPreloaded"
|
||||
@tableBuilt="funktionenTableBuilt"
|
||||
:title="$p.t('person','funktionen')"
|
||||
ref="funktionenTable"
|
||||
@@ -466,7 +463,6 @@ export default {
|
||||
<!-- BETRIEBSMITTEL TABELLE -->
|
||||
<div class="col-12 mb-4" >
|
||||
<core-filter-cmpt
|
||||
v-if="arePhrasesPreloaded"
|
||||
@tableBuilt="betriebsmittelTableBuilt"
|
||||
:title="$p.t('profil','entlehnteBetriebsmittel')"
|
||||
ref="betriebsmittelTable"
|
||||
|
||||
@@ -20,7 +20,6 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
collapseIconFunktionen: true,
|
||||
arePhrasesPreloaded: false,
|
||||
funktionen_table_options: {
|
||||
persistenceID: "filterTableMaViewProfilFunktionen",
|
||||
persistence: {
|
||||
@@ -31,6 +30,7 @@ export default {
|
||||
responsiveLayout: "collapse",
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
locale: true,
|
||||
columns: [
|
||||
//? option when wanting to hide the collapsed list
|
||||
|
||||
@@ -45,25 +45,24 @@ export default {
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t("ui/bezeichnung")),
|
||||
title: "placeholder",
|
||||
titlePhrase: "ui/bezeichnung",
|
||||
field: "Bezeichnung",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() =>
|
||||
this.$p.t("lehre/organisationseinheit"),
|
||||
),
|
||||
title: "placeholder",
|
||||
titlePhrase: "lehre/organisationseinheit",
|
||||
field: "Organisationseinheit",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() =>
|
||||
this.$p.t("global/gueltigVon"),
|
||||
),
|
||||
title: "placeholder",
|
||||
titlePhrase: "global/gueltigVon",
|
||||
field: "Gültig_von",
|
||||
headerFilterFunc: "dates",
|
||||
headerFilter: dateFilter,
|
||||
@@ -74,9 +73,8 @@ export default {
|
||||
formatterParams: this.datetimeFormatterParams(),
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() =>
|
||||
this.$p.t("global/gueltigBis"),
|
||||
),
|
||||
title: "placeholder",
|
||||
titlePhrase: "global/gueltigBis",
|
||||
field: "Gültig_bis",
|
||||
headerFilterFunc: "dates",
|
||||
headerFilter: dateFilter,
|
||||
@@ -87,9 +85,8 @@ export default {
|
||||
formatterParams: this.datetimeFormatterParams(),
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() =>
|
||||
this.$p.t("profil/wochenstunden"),
|
||||
),
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/wochenstunden",
|
||||
field: "Wochenstunden",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
@@ -214,11 +211,6 @@ export default {
|
||||
return quickLinks;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$p.loadCategory(["ui", "lehre", "global", "profil"]).then(() => {
|
||||
this.arePhrasesPreloaded = true;
|
||||
});
|
||||
},
|
||||
|
||||
template: /*html*/ `
|
||||
|
||||
@@ -273,7 +265,7 @@ export default {
|
||||
<div class="row">
|
||||
<!-- FIRST TABLE -->
|
||||
<div class="col-12 mb-4" >
|
||||
<core-filter-cmpt v-if="arePhrasesPreloaded" @tableBuilt="funktionenTableBuilt" :title="$p.t('person','funktionen')" ref="funktionenTable" :tabulator-options="funktionen_table_options" tableOnly :sideMenu="false" />
|
||||
<core-filter-cmpt @tableBuilt="funktionenTableBuilt" :title="$p.t('person','funktionen')" ref="funktionenTable" :tabulator-options="funktionen_table_options" tableOnly :sideMenu="false" />
|
||||
</div>
|
||||
<!-- END OF THE ROW WITH THE TABLES UNDER THE PROFIL INFORMATION -->
|
||||
</div>
|
||||
|
||||
@@ -29,13 +29,12 @@ export default {
|
||||
QuickLinks,
|
||||
CalendarSync,
|
||||
},
|
||||
inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"],
|
||||
inject: ["sortProfilUpdates", "collapseFunction", "isEditable"],
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
collapseIconBetriebsmittel: true,
|
||||
editDataFilter: null,
|
||||
arePhrasesPreloaded: false,
|
||||
// tabulator options
|
||||
zutrittsgruppen_table_options: {
|
||||
persistenceID: "filterTableStudentProfilZutrittsgruppen",
|
||||
@@ -44,9 +43,9 @@ export default {
|
||||
},
|
||||
minHeight: 200,
|
||||
layout: "fitColumns",
|
||||
columns: [
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('profil/zutrittsGruppen')),
|
||||
columns: [{
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/zutrittsGruppen",
|
||||
field: "bezeichnung"
|
||||
}
|
||||
],
|
||||
@@ -62,6 +61,7 @@ export default {
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
responsiveLayoutCollapseStartOpen: false,
|
||||
locale: true,
|
||||
columns: [
|
||||
{
|
||||
title:
|
||||
@@ -75,7 +75,8 @@ export default {
|
||||
responsive: 0,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(()=>this.$p.t('profil/entlehnteBetriebsmittel')),
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/entlehnteBetriebsmittel",
|
||||
field: "betriebsmittel",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
@@ -83,7 +84,8 @@ export default {
|
||||
responsive: 0,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('profil/inventarnummer')) ,
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/inventarnummer",
|
||||
field: "Nummer",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
@@ -92,7 +94,8 @@ export default {
|
||||
responsive: 2,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('profil/ausgabedatum')) ,
|
||||
title: "placeholder",
|
||||
titlePhrase: "profil/ausgabedatum",
|
||||
field: "Ausgegeben_am",
|
||||
headerFilterFunc: 'dates',
|
||||
headerFilter: dateFilter,
|
||||
@@ -259,19 +262,9 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// preload phrasen
|
||||
this.$p.loadCategory('profil').then(() => {
|
||||
this.arePhrasesPreloaded = true;
|
||||
});
|
||||
//? sorts the profil Updates: pending -> accepted -> rejected
|
||||
this.data.profilUpdates?.sort(this.sortProfilUpdates);
|
||||
},
|
||||
watch: {
|
||||
'language.value'(newVal) {
|
||||
if(this.$refs.betriebsmittelTable) this.$refs.betriebsmittelTable.tabulator.setColumns(this.betriebsmittel_table_options.columns)
|
||||
if(this.$refs.zutrittsgruppenTable) this.$refs.zutrittsgruppenTable.tabulator.setColumns(this.zutrittsgruppen_table_options.columns)
|
||||
}
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="container-fluid text-break fhc-form">
|
||||
<edit-profil v-if="showModal" ref="editModal" @hideBsModal="hideEditProfilModal"
|
||||
@@ -395,7 +388,6 @@ export default {
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4" >
|
||||
<core-filter-cmpt
|
||||
v-if="arePhrasesPreloaded"
|
||||
@tableBuilt="betriebsmittelTableBuilt"
|
||||
:title="$p.t('profil','entlehnteBetriebsmittel')"
|
||||
ref="betriebsmittelTable"
|
||||
@@ -405,7 +397,6 @@ export default {
|
||||
</div>
|
||||
<div class="col-12 mb-4" >
|
||||
<core-filter-cmpt
|
||||
v-if="arePhrasesPreloaded"
|
||||
@tableBuilt="zutrittsgruppenTableBuilt"
|
||||
:title="$p.t('profil','zutrittsGruppen')"
|
||||
ref="zutrittsgruppenTable"
|
||||
|
||||
@@ -22,8 +22,14 @@ import TableDownload from './Table/Download.js';
|
||||
import collapseAutoClose from '../../directives/collapseAutoClose.js';
|
||||
|
||||
import moduleLayoutFitDataStretchFrozen from '../../tabulator/layouts/fitDataStretchFrozen.js';
|
||||
import InternalToExternalEventBroadcastModule from "../../tabulator/customModules/InternalToExternalEventBroadcastModule.js"
|
||||
import MenuExtensionModule from "../../tabulator/customModules/MenuExtensionModule.js"
|
||||
import ResponsiveLayoutExtensionModule from "../../tabulator/customModules/ResponsiveLayoutExtensionModule.js"
|
||||
|
||||
import { debounce } from "../../helpers/DebounceHelper.js";
|
||||
|
||||
import ApiFilter from '../../api/factory/filter.js';
|
||||
import ApiPhrases from '../../api/factory/phrasen.js';
|
||||
|
||||
//
|
||||
const FILTER_COMPONENT_NEW_FILTER = 'Filter Component New Filter';
|
||||
@@ -84,8 +90,9 @@ export const CoreFilterCmpt = {
|
||||
useSelectionSpan: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
},
|
||||
inject: ["language"],
|
||||
data: function() {
|
||||
return {
|
||||
uuid: 0,
|
||||
@@ -120,7 +127,8 @@ export const CoreFilterCmpt = {
|
||||
headerFilter: false,
|
||||
group: false,
|
||||
page: false,
|
||||
}
|
||||
},
|
||||
collapsedHeadingLocalizationTimer: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -183,20 +191,25 @@ export const CoreFilterCmpt = {
|
||||
return columns;
|
||||
},
|
||||
fieldIdsForVisibilty() {
|
||||
if (!this.tableBuilt)
|
||||
return [];
|
||||
return this.tabulator.getColumns().filter(col => {
|
||||
let def = col.getDefinition();
|
||||
return !def.frozen && def.title && def.formatter != "responsiveCollapse";
|
||||
}).map(col => col.getField());
|
||||
},
|
||||
fieldNames() {
|
||||
if (!this.tableBuilt)
|
||||
return {};
|
||||
return this.tabulator.getColumns().reduce((res, col) => {
|
||||
res[col.getField()] = col.getDefinition().title;
|
||||
return res;
|
||||
}, {});
|
||||
if (!this.tableBuilt) return [];
|
||||
|
||||
const localizedColumnTitles = this.tabulator.getLang().columns;
|
||||
const isTabulatorLocalized = !!this.$props.tabulatorOptions.locale;
|
||||
return this.tabulator
|
||||
.getColumns()
|
||||
.filter((col) => {
|
||||
let def = col.getDefinition();
|
||||
let title =
|
||||
isTabulatorLocalized && localizedColumnTitles[def.field]
|
||||
? localizedColumnTitles[def.field]
|
||||
: def.title;
|
||||
return (
|
||||
!def.frozen &&
|
||||
title &&
|
||||
def.formatter != "responsiveCollapse"
|
||||
);
|
||||
})
|
||||
.map((col) => col.getField());
|
||||
},
|
||||
idExtra() {
|
||||
if (!this.uuid)
|
||||
@@ -204,14 +217,30 @@ export const CoreFilterCmpt = {
|
||||
return '-' + this.uuid;
|
||||
},
|
||||
columnsForFilter() {
|
||||
if (!this.filteredColumns || !this.datasetMetadata)
|
||||
return [];
|
||||
const filterTitles = this.filteredColumns.reduce((a,c) => {
|
||||
a[c.field] = c.title;
|
||||
if (!this.filteredColumns || !this.datasetMetadata) return [];
|
||||
const localizedColumnTitles = this.tabulator.getLang().columns;
|
||||
const isTabulatorLocalized = !!this.$props.tabulatorOptions.locale;
|
||||
const filterTitles = this.filteredColumns.reduce((a, c) => {
|
||||
a[c.field] =
|
||||
isTabulatorLocalized && localizedColumnTitles[c.field]
|
||||
? localizedColumnTitles[c.field]
|
||||
: c.title;
|
||||
return a;
|
||||
}, {});
|
||||
return this.datasetMetadata.map(el => ({...el, ...{title: filterTitles[el.name]}}));
|
||||
}
|
||||
return this.datasetMetadata.map((el) => ({
|
||||
...el,
|
||||
...{ title: filterTitles[el.name] },
|
||||
}));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
"language.value": {
|
||||
handler(newSelectedLanguage) {
|
||||
if (!this.$props.tabulatorOptions.locale) return;
|
||||
|
||||
this.tabulator.setLocale(newSelectedLanguage);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
reloadTable() {
|
||||
@@ -267,6 +296,10 @@ export const CoreFilterCmpt = {
|
||||
persistence: this.persistence,
|
||||
}, ...(this.tabulatorOptions || {})};
|
||||
|
||||
if (tabulatorOptions.locale) {
|
||||
tabulatorOptions = await this.configureTabulatorLocalizations(tabulatorOptions);
|
||||
}
|
||||
|
||||
// set default height if no height property is set
|
||||
if (tabulatorOptions.height === undefined &&
|
||||
tabulatorOptions.minHeight === undefined &&
|
||||
@@ -295,10 +328,10 @@ export const CoreFilterCmpt = {
|
||||
}
|
||||
|
||||
// Start the tabulator with the build options
|
||||
this.tabulator = new Tabulator(
|
||||
this.$refs.table,
|
||||
tabulatorOptions
|
||||
);
|
||||
this.tabulator = new Tabulator(this.$refs.table, {
|
||||
...tabulatorOptions,
|
||||
debugInvalidOptions: false,
|
||||
});
|
||||
// If event handlers have been provided
|
||||
if (Array.isArray(this.tabulatorEvents) && this.tabulatorEvents.length > 0)
|
||||
{
|
||||
@@ -306,7 +339,10 @@ export const CoreFilterCmpt = {
|
||||
for (let evt of this.tabulatorEvents)
|
||||
this.tabulator.on(evt.event, evt.handler);
|
||||
}
|
||||
this.tabulator.on('tableBuilt', () => {this.tableBuilt = true; this.$emit('tableBuilt');});
|
||||
this.tabulator.on('tableBuilt', () => {
|
||||
this.tableBuilt = true;
|
||||
this.$emit('tableBuilt');
|
||||
});
|
||||
this.tabulator.on("rowSelectionChanged", data => {
|
||||
this.selectedData = data;
|
||||
});
|
||||
@@ -352,6 +388,121 @@ export const CoreFilterCmpt = {
|
||||
this.filterActive = filters.length > 0;
|
||||
this.$emit("headerFilterOn", this.filterActive);
|
||||
});
|
||||
this.tabulator.on("localized", () => {
|
||||
this.tabulator.modules.responsiveLayout.generateCollapsedContent();
|
||||
});
|
||||
},
|
||||
async configureTabulatorLocalizations(tabulatorOptions) {
|
||||
tabulatorOptions.locale = this.$p.user_language.value;
|
||||
tabulatorOptions.langs = {};
|
||||
|
||||
const genericTabulatorPhrasesResponse = await this.$api.call(
|
||||
ApiPhrases.getTabulatorPhrases(),
|
||||
);
|
||||
const genericTabulatorPhrasesData = genericTabulatorPhrasesResponse.data;
|
||||
Object.keys(genericTabulatorPhrasesData).forEach((language) => {
|
||||
let genericTabulatorPhraseToTranslationMapper = {};
|
||||
genericTabulatorPhrasesData[language].forEach((phrase) => {
|
||||
genericTabulatorPhraseToTranslationMapper[phrase.phrase] = phrase.text;
|
||||
});
|
||||
|
||||
tabulatorOptions.langs[language] = {
|
||||
data: {
|
||||
loading: genericTabulatorPhraseToTranslationMapper["loading"],
|
||||
error: genericTabulatorPhraseToTranslationMapper["error"],
|
||||
},
|
||||
groups: {
|
||||
item: genericTabulatorPhraseToTranslationMapper["item"],
|
||||
items: genericTabulatorPhraseToTranslationMapper["items"],
|
||||
},
|
||||
pagination: {
|
||||
page_size:
|
||||
genericTabulatorPhraseToTranslationMapper["page_size"],
|
||||
page_title:
|
||||
genericTabulatorPhraseToTranslationMapper["page_title"],
|
||||
first: genericTabulatorPhraseToTranslationMapper["first"],
|
||||
first_title:
|
||||
genericTabulatorPhraseToTranslationMapper["first_title"],
|
||||
last: genericTabulatorPhraseToTranslationMapper["last"],
|
||||
last_title:
|
||||
genericTabulatorPhraseToTranslationMapper["last_title"],
|
||||
prev: genericTabulatorPhraseToTranslationMapper["prev"],
|
||||
prev_title:
|
||||
genericTabulatorPhraseToTranslationMapper["prev_title"],
|
||||
next: genericTabulatorPhraseToTranslationMapper["next"],
|
||||
next_title:
|
||||
genericTabulatorPhraseToTranslationMapper["next_title"],
|
||||
all: genericTabulatorPhraseToTranslationMapper["all"],
|
||||
counter: {
|
||||
showing:
|
||||
genericTabulatorPhraseToTranslationMapper["showing"],
|
||||
of: genericTabulatorPhraseToTranslationMapper["of"],
|
||||
rows: genericTabulatorPhraseToTranslationMapper["rows"],
|
||||
pages: genericTabulatorPhraseToTranslationMapper["pages"],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
let phrasesGroupedByCategoryRequestParam = {};
|
||||
tabulatorOptions.columns.forEach((column) => {
|
||||
if (!column.titlePhrase?.length) return;
|
||||
|
||||
let [category, phrase] = column.titlePhrase.split("/");
|
||||
if (phrasesGroupedByCategoryRequestParam[category]) {
|
||||
phrasesGroupedByCategoryRequestParam[category].push(phrase);
|
||||
} else {
|
||||
phrasesGroupedByCategoryRequestParam[category] = [phrase];
|
||||
}
|
||||
});
|
||||
tabulatorOptions.menuItemPhrases?.forEach((menuItemPhrase) => {
|
||||
let [category, phrase] = menuItemPhrase.split("/");
|
||||
if (phrasesGroupedByCategoryRequestParam[category]) {
|
||||
phrasesGroupedByCategoryRequestParam[category].push(phrase);
|
||||
} else {
|
||||
phrasesGroupedByCategoryRequestParam[category] = [phrase];
|
||||
}
|
||||
});
|
||||
|
||||
if (!Object.keys(phrasesGroupedByCategoryRequestParam).length) {
|
||||
return tabulatorOptions;
|
||||
}
|
||||
|
||||
const phrasesResponse = await this.$api.call(
|
||||
ApiPhrases.getPhrases(phrasesGroupedByCategoryRequestParam),
|
||||
);
|
||||
const phrasesData = phrasesResponse.data;
|
||||
|
||||
Object.keys(phrasesData).forEach((language) => {
|
||||
let phraseToTranslationMapper = {};
|
||||
phrasesData[language].forEach((phrase) => {
|
||||
phraseToTranslationMapper[
|
||||
phrase.category + "/" + phrase.phrase
|
||||
] = phrase.text;
|
||||
});
|
||||
|
||||
let columnFieldToTranslationMapper = {};
|
||||
tabulatorOptions.columns.forEach((column) => {
|
||||
if (!column.titlePhrase?.length) return;
|
||||
|
||||
columnFieldToTranslationMapper[column.field] =
|
||||
phraseToTranslationMapper[column.titlePhrase] ??
|
||||
"<< PHRASE " + column.titlePhrase + " >>";
|
||||
});
|
||||
tabulatorOptions.langs[language].columns =
|
||||
columnFieldToTranslationMapper;
|
||||
|
||||
let menuItemPhraseToTranslationMapper = {};
|
||||
tabulatorOptions.menuItemPhrases?.forEach((menuItemPhrase) => {
|
||||
menuItemPhraseToTranslationMapper[menuItemPhrase] =
|
||||
phraseToTranslationMapper[menuItemPhrase] ??
|
||||
"<< PHRASE " + menuItemPhrase + " >>";
|
||||
});
|
||||
tabulatorOptions.langs[language].menuItems =
|
||||
menuItemPhraseToTranslationMapper;
|
||||
});
|
||||
|
||||
return tabulatorOptions;
|
||||
},
|
||||
updateTabulator() {
|
||||
if (this.tabulator) {
|
||||
@@ -659,7 +810,18 @@ export const CoreFilterCmpt = {
|
||||
|
||||
// parent not found
|
||||
return false;
|
||||
}
|
||||
},
|
||||
getColumnNames() {
|
||||
if (!this.tableBuilt) {
|
||||
return {};
|
||||
} else if (this.tabulator.options.locale) {
|
||||
return this.tabulator.getLang().columns;
|
||||
}
|
||||
return this.tabulator.getColumns().reduce((res, col) => {
|
||||
res[col.getField()] = col.getDefinition().title;
|
||||
return res;
|
||||
}, {});
|
||||
},
|
||||
},
|
||||
beforeCreate() {
|
||||
if (!this.tableOnly == !this.filterType)
|
||||
@@ -670,6 +832,10 @@ export const CoreFilterCmpt = {
|
||||
alert('"nwNewEntry" listener is mandatory when sideMenu is true');
|
||||
this.uuid = _uuid++;
|
||||
this.$emit('uuidDefined', this.uuid)
|
||||
|
||||
Tabulator.registerModule(InternalToExternalEventBroadcastModule);
|
||||
Tabulator.registerModule(MenuExtensionModule);
|
||||
Tabulator.registerModule(ResponsiveLayoutExtensionModule);
|
||||
},
|
||||
mounted() {
|
||||
this.initTabulator().then(() => {
|
||||
@@ -737,7 +903,7 @@ export const CoreFilterCmpt = {
|
||||
:data-bs-parent="'#filterCollapsables' + idExtra"
|
||||
:fields="fieldIdsForVisibilty"
|
||||
:selected="selectedFields"
|
||||
:names="fieldNames"
|
||||
:names="getColumnNames()"
|
||||
@hide="tabulator.hideColumn($event)"
|
||||
@show="tabulator.showColumn($event)"
|
||||
v-collapse-auto-close
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
watch: {
|
||||
selected(n) {
|
||||
this.selectedFields = n;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggle(field) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import dms from "./result/dms.js";
|
||||
import cms from "./result/cms.js";
|
||||
import mergedStudent from "./result/mergedstudent.js";
|
||||
import mergedPerson from "./result/mergedperson.js";
|
||||
import { debounce } from "../../helpers/DebounceHelper.js";
|
||||
|
||||
export default {
|
||||
name: "FhcSearchbar",
|
||||
@@ -57,6 +58,107 @@ export default {
|
||||
settingsDropdown: null,
|
||||
lastQuery: "",
|
||||
isSearchShownInMobileView: false,
|
||||
callsearchapi: debounce(() => {
|
||||
this.error = null;
|
||||
this.searchresult.splice(0, this.searchresult.length);
|
||||
this.searching = true;
|
||||
this.showsearchresult();
|
||||
if (this.searchsettings.types.length === 0) {
|
||||
this.error = this.$p.t("search/error_missing_type");
|
||||
this.searching = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.abortController) this.abortController.abort();
|
||||
|
||||
if (!this.searchsettings.searchstr?.length) return;
|
||||
|
||||
this.abortController = new AbortController();
|
||||
|
||||
this.searchfunction(this.searchsettings, {
|
||||
timeout: 50000,
|
||||
signal: this.abortController.signal,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.data) {
|
||||
this.error = this.$p.t("search/error_general");
|
||||
} else {
|
||||
let res = response.data.map((el) =>
|
||||
el.data ? { ...el, ...JSON.parse(el.data) } : el,
|
||||
);
|
||||
this.lastQuery = response.meta.searchstring;
|
||||
if (this.searchoptions.mergeResults) {
|
||||
let counter = 0;
|
||||
let mergeTypes = [];
|
||||
let mergedType = "merged-";
|
||||
let mergeKey = "";
|
||||
|
||||
switch (this.searchoptions.mergeResults) {
|
||||
case "student":
|
||||
mergeTypes = ["student", "prestudent"];
|
||||
mergedType +=
|
||||
this.searchoptions.mergeResults;
|
||||
mergeKey = "uid";
|
||||
break;
|
||||
case "person":
|
||||
mergeTypes = [
|
||||
"person",
|
||||
"employee",
|
||||
"student",
|
||||
"prestudent",
|
||||
];
|
||||
mergedType +=
|
||||
this.searchoptions.mergeResults;
|
||||
mergeKey = "person_id";
|
||||
break;
|
||||
}
|
||||
|
||||
if (mergeTypes.length) {
|
||||
res = Object.values(
|
||||
res.reduce((a, c) => {
|
||||
if (!mergeTypes.includes(c.renderer)) {
|
||||
a["nomerge" + counter++] = c;
|
||||
} else if (c[mergeKey] === null) {
|
||||
a["nomerge" + counter++] = c;
|
||||
} else if (
|
||||
a[c[mergeKey]] === undefined
|
||||
) {
|
||||
a[c[mergeKey]] = {
|
||||
rank: c.rank,
|
||||
renderer: mergedType,
|
||||
type: mergedType,
|
||||
list: [c],
|
||||
};
|
||||
} else {
|
||||
a[c[mergeKey]].list.push(c);
|
||||
if (c.rank > a[c[mergeKey]].rank)
|
||||
a[c[mergeKey]].rank = c.rank;
|
||||
}
|
||||
return a;
|
||||
}, {}),
|
||||
).sort((a, b) => b.rank - a.rank);
|
||||
}
|
||||
}
|
||||
this.searchresult = res;
|
||||
this.searchmode = response.meta.mode;
|
||||
}
|
||||
this.searching = false;
|
||||
this.retry = 0;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.code == "ERR_CANCELED") {
|
||||
return (this.retry = 0);
|
||||
}
|
||||
if (error.code == "ECONNABORTED" && this.retry) {
|
||||
this.retry--;
|
||||
return this.callsearchapi();
|
||||
}
|
||||
|
||||
this.error = this.$p.t("search/error_general", error);
|
||||
this.searching = false;
|
||||
this.retry = 0;
|
||||
});
|
||||
}, 500),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -345,122 +447,18 @@ export default {
|
||||
this.abort();
|
||||
if (this.searchsettings.searchstr.length >= 2) {
|
||||
this.calcSearchResultExtent();
|
||||
this.searchtimer = setTimeout(this.callsearchapi, 500);
|
||||
this.callsearchapi();
|
||||
} else {
|
||||
this.showresult = false;
|
||||
}
|
||||
},
|
||||
abort() {
|
||||
if (this.searchtimer !== null) {
|
||||
clearTimeout(this.searchtimer);
|
||||
}
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
this.abortController = null;
|
||||
}
|
||||
this.searchresult = [];
|
||||
},
|
||||
callsearchapi: function () {
|
||||
this.error = null;
|
||||
this.searchresult.splice(0, this.searchresult.length);
|
||||
this.searching = true;
|
||||
this.showsearchresult();
|
||||
if (this.searchsettings.types.length === 0) {
|
||||
this.error = this.$p.t("search/error_missing_type");
|
||||
this.searching = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.abortController) this.abortController.abort();
|
||||
|
||||
if (!this.searchsettings.searchstr?.length) return;
|
||||
|
||||
this.abortController = new AbortController();
|
||||
|
||||
this.searchfunction(this.searchsettings, {
|
||||
timeout: 50000,
|
||||
signal: this.abortController.signal,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.data) {
|
||||
this.error = this.$p.t("search/error_general");
|
||||
} else {
|
||||
let res = response.data.map((el) =>
|
||||
el.data ? { ...el, ...JSON.parse(el.data) } : el,
|
||||
);
|
||||
this.lastQuery = response.meta.searchstring;
|
||||
if (this.searchoptions.mergeResults) {
|
||||
let counter = 0;
|
||||
let mergeTypes = [];
|
||||
let mergedType = "merged-";
|
||||
let mergeKey = "";
|
||||
|
||||
switch (this.searchoptions.mergeResults) {
|
||||
case "student":
|
||||
mergeTypes = ["student", "prestudent"];
|
||||
mergedType +=
|
||||
this.searchoptions.mergeResults;
|
||||
mergeKey = "uid";
|
||||
break;
|
||||
case "person":
|
||||
mergeTypes = [
|
||||
"person",
|
||||
"employee",
|
||||
"student",
|
||||
"prestudent",
|
||||
];
|
||||
mergedType +=
|
||||
this.searchoptions.mergeResults;
|
||||
mergeKey = "person_id";
|
||||
break;
|
||||
}
|
||||
|
||||
if (mergeTypes.length) {
|
||||
res = Object.values(
|
||||
res.reduce((a, c) => {
|
||||
if (!mergeTypes.includes(c.renderer)) {
|
||||
a["nomerge" + counter++] = c;
|
||||
} else if (c[mergeKey] === null) {
|
||||
a["nomerge" + counter++] = c;
|
||||
} else if (
|
||||
a[c[mergeKey]] === undefined
|
||||
) {
|
||||
a[c[mergeKey]] = {
|
||||
rank: c.rank,
|
||||
renderer: mergedType,
|
||||
type: mergedType,
|
||||
list: [c],
|
||||
};
|
||||
} else {
|
||||
a[c[mergeKey]].list.push(c);
|
||||
if (c.rank > a[c[mergeKey]].rank)
|
||||
a[c[mergeKey]].rank = c.rank;
|
||||
}
|
||||
return a;
|
||||
}, {}),
|
||||
).sort((a, b) => b.rank - a.rank);
|
||||
}
|
||||
}
|
||||
this.searchresult = res;
|
||||
this.searchmode = response.meta.mode;
|
||||
}
|
||||
this.searching = false;
|
||||
this.retry = 0;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.code == "ERR_CANCELED") {
|
||||
return (this.retry = 0);
|
||||
}
|
||||
if (error.code == "ECONNABORTED" && this.retry) {
|
||||
this.retry--;
|
||||
return this.callsearchapi();
|
||||
}
|
||||
|
||||
this.error = this.$p.t("search/error_general", error);
|
||||
this.searching = false;
|
||||
this.retry = 0;
|
||||
});
|
||||
},
|
||||
refreshsearch: function () {
|
||||
this.search();
|
||||
this.togglesettings();
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export const debounce = (callback, wait) => {
|
||||
let timeoutId = null;
|
||||
return (context) => {
|
||||
window.clearTimeout(timeoutId);
|
||||
timeoutId = window.setTimeout(() => callback(), wait);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,49 @@
|
||||
export function capitalize(string) {
|
||||
if (!string) return '';
|
||||
if (!string) return "";
|
||||
return string[0].toUpperCase() + string.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
export function convertCase(string, from, to) {
|
||||
const possibleCases = ["kebab", "snake", "camel"];
|
||||
if (
|
||||
from === to ||
|
||||
!possibleCases.includes(from) ||
|
||||
!possibleCases.includes(to)
|
||||
)
|
||||
return string;
|
||||
|
||||
const individualWords =
|
||||
from === "kebab"
|
||||
? string.split("-")
|
||||
: from === "snake"
|
||||
? string.split("_")
|
||||
: splitWordsInCamelCase(string);
|
||||
|
||||
const formattedIndividualWords = ["snake", "kebab"].includes(to)
|
||||
? individualWords.map((word) => word.toLowerCase())
|
||||
: individualWords.map((word, index) => {
|
||||
if (!index) return word.toLowerCase();
|
||||
return word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase();
|
||||
});
|
||||
|
||||
const joinCharacter = to === "snake" ? "_" : to === "kebab" ? "-" : "";
|
||||
return formattedIndividualWords.join(joinCharacter);
|
||||
}
|
||||
|
||||
function splitWordsInCamelCase(string) {
|
||||
const lastUpperCaseLetterIndex = string
|
||||
.split("")
|
||||
.findLastIndex((character) => {
|
||||
return character.toUpperCase() === character;
|
||||
});
|
||||
|
||||
if (lastUpperCaseLetterIndex < 1) {
|
||||
return [string];
|
||||
}
|
||||
|
||||
let splitWords = splitWordsInCamelCase(
|
||||
string.slice(0, lastUpperCaseLetterIndex),
|
||||
);
|
||||
splitWords.push(string.slice(lastUpperCaseLetterIndex));
|
||||
return splitWords;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// changes within tabulator are often not adequately broadcast to the external component
|
||||
// this module exposes tabulator's internal events
|
||||
// if external event name is not specified, the dispatched external event will be the camelCase version of the kebab-case internal event
|
||||
|
||||
import Module from "../../../../vendor/olifolkerd/tabulator5/src/js/core/Module.js";
|
||||
import { convertCase } from "../../helpers/StringHelpers.js";
|
||||
|
||||
export default class InternalToExternalEventBroadcastModule extends Module {
|
||||
static moduleName = "internalToExternalEventBroadcast";
|
||||
static moduleInitNumber = 1;
|
||||
|
||||
constructor(table) {
|
||||
super(table);
|
||||
|
||||
this.eventsToBroadcast = [
|
||||
// example event
|
||||
// {
|
||||
// internal: "layout-refreshed",
|
||||
// external: null,
|
||||
// },
|
||||
];
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.eventsToBroadcast.forEach((event) => {
|
||||
this.subscribe(event.internal, () => {
|
||||
this.dispatchExternal(event.external ?? convertCase(event.internal, "kebab", "camel"));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import Menu from "../../../../vendor/olifolkerd/tabulator5/src/js/modules/Menu/Menu.js";
|
||||
|
||||
export default class MenuExtensionModule extends Menu {
|
||||
static moduleName = "menu";
|
||||
static moduleInitNumber = 1;
|
||||
|
||||
constructor(table) {
|
||||
super(table);
|
||||
}
|
||||
|
||||
loadMenu(e, component, menu, parentEl, parentPopup){
|
||||
const isLocalizationEnabled = component.table.options.locale && component.table.options.locale !== "default";
|
||||
const menuItemTranslations = component.table.getLang().menuItems;
|
||||
if (isLocalizationEnabled && menuItemTranslations) {
|
||||
menu = menu.map((menuItem) => {
|
||||
if (menuItem.phrase && menuItemTranslations[menuItem.phrase]) {
|
||||
menuItem.label = menuItemTranslations[menuItem.phrase];
|
||||
}
|
||||
return menuItem;
|
||||
});
|
||||
}
|
||||
|
||||
super.loadMenu(e, component, menu, parentEl, parentPopup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import ResponsiveLayout from "../../../../vendor/olifolkerd/tabulator5/src/js/modules/ResponsiveLayout/ResponsiveLayout.js";
|
||||
|
||||
export default class ResponsiveLayoutExtensionModule extends ResponsiveLayout {
|
||||
static moduleName = "responsiveLayout";
|
||||
static moduleInitNumber = 1;
|
||||
|
||||
constructor(table) {
|
||||
super(table);
|
||||
}
|
||||
|
||||
generateCollapsedRowData(row) {
|
||||
let result = super.generateCollapsedRowData(row)
|
||||
const isLocalizationEnabled = this.table.options.locale && this.table.options.locale !== "default";
|
||||
const columnHeadingTranslations = this.table?.getLang()?.columns;
|
||||
|
||||
if (isLocalizationEnabled && columnHeadingTranslations) {
|
||||
result = result.map((column) => {
|
||||
if (columnHeadingTranslations[column.field]) {
|
||||
column.title = columnHeadingTranslations[column.field];
|
||||
}
|
||||
return column;
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -59958,6 +59958,388 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
)
|
||||
),
|
||||
// ### End Notenstatistik
|
||||
// generic tabulator phrases START
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'loading',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Lädt',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Loading',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'error',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Fehler',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Error',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'item',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Element',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'item',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'items',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Elemente',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'items',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'page_size',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Seitengröße',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Page Size',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'page_title',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Seite anzeigen',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Show Page',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'first',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Erste',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'First',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'first_title',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Erste Seite',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'First Page',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'last',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Letzte',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Last',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'last_title',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Letzte Seite',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Last Page',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'prev',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'zurück',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Previous',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'prev_title',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'vorherige Seite',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Previous Page',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'next',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Nächste',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Next',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'next_title',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Nächste Seite',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Next Page',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'all',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Alle',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'All',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'showing',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Angezeigt',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Showing',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'of',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'von',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'of',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'rows',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Zeilen',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'rows',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'tabulator',
|
||||
'phrase' => 'pages',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Seiten',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'pages',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
// generic tabulator phrases END
|
||||
);
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user