Merge branch 'feature/76688-general-tabulator-issues_77080-persistence' into studvw_lvvw_2026_08_rc_77973

This commit is contained in:
Harald Bamberger
2026-08-03 16:06:34 +02:00
12 changed files with 1739 additions and 203 deletions
@@ -0,0 +1,162 @@
<?php
/**
* Copyright (C) 2024 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');
/**
* This controller operates between (interface) the JS (GUI) and the PhrasesLib (back-end)
* Provides data to the ajax get calls about the Phrasen plugin
* This controller works with JSON calls on the HTTP GET and the output is always JSON
*/
class TabulatorPresets extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getTabulatorPresets' => self::PERM_LOGGED,
'createTabulatorPreset' => self::PERM_LOGGED,
'updateTabulatorPreset' => self::PERM_LOGGED,
'deleteTabulatorPreset' => self::PERM_LOGGED,
]);
$this->load->model('tabulator/Tabulator_preset_model', 'TabulatorPresetModel');
$this->loadPhrases([
'global',
'tabulator_presets'
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getTabulatorPresets()
{
$tableName = $this->input->get("tableName");
if (!$tableName) {
$this->terminateWithError(error($this->p->t("global", "invalid_params_err_msg")));
}
$uid = getAuthUID();
$presets = $this->TabulatorPresetModel->getTabulatorPresetsByUserAndTable($uid, $tableName);
$presetsData = $this->getDataOrTerminateWithError($presets) ?? [];
$this->terminateWithSuccess($presetsData);
}
public function createTabulatorPreset()
{
$tableName = $this->input->post("tableName");
$presetName = $this->input->post("presetName");
$preset = $this->input->post("preset");
if (!$tableName || !$presetName || !$preset) {
$this->terminateWithError($this->p->t("global", "invalid_params_err_msg"), "general", 400);
}
$uid = getAuthUID();
$existingPresetsResult = $this->TabulatorPresetModel->getTabulatorPresetsByUserAndTable($uid, $tableName);
$existingPresets = $this->getDataOrTerminateWithError($existingPresetsResult) ?? [];
if (count($existingPresets) > 19) {
$this->terminateWithError($this->p->t("tabulator_presets", "max_presets_count_err_msg"), "general", 409);
}
$existingPresetNames = array_map(function($presetInfo) {
return $presetInfo->preset_name;
}, $existingPresets);
if (in_array($presetName, $existingPresetNames)) {
$this->terminateWithError($this->p->t("tabulator_presets", "preset_name_duplicate_err_msg"), "general", 409);
}
$presetCreationResult = $this->TabulatorPresetModel->createTabulatorPreset($uid, $tableName, $presetName, $preset);
if (isError($presetCreationResult)) {
$this->terminateWithError($presetCreationResult->retval);
}
$newPresetData = $this->TabulatorPresetModel->getTabulatorPreset($presetCreationResult->retval);
$newPresetArray = $this->getDataOrTerminateWithError($newPresetData) ?? [null];
$newPreset = $newPresetArray[0];
$this->terminateWithSuccess($newPreset);
}
public function updateTabulatorPreset()
{
$presetId = $this->input->post("presetId");
$preset = $this->input->post("preset");
if (!$presetId || !$preset) {
$this->terminateWithError($this->p->t("global", "invalid_params_err_msg"), "general", 400);
}
$existingPresetData = $this->TabulatorPresetModel->getTabulatorPreset($presetId);
$existingPresetArray = $this->getDataOrTerminateWithError($existingPresetData) ?? [null];
$existingPreset = $existingPresetArray[0];
if (!$existingPreset) {
$this->terminateWithError($this->p->t("tabulator_presets", "preset_not_found_err_msg"), "general", 404);
}
$uid = getAuthUID();
if ($existingPreset->benutzer_uid !== $uid) {
$this->terminateWithError($this->p->t("tabulator_presets", "preset_not_own_update_err_msg"), "general", 403);
}
$presetUpdateResult = $this->TabulatorPresetModel->updateTabulatorPreset($presetId, $preset);
if (isError($presetUpdateResult)) {
$this->terminateWithError($presetUpdateResult->retval);
}
$updatedPresetData = $this->TabulatorPresetModel->getTabulatorPreset($presetId);
$updatedPresetArray = $this->getDataOrTerminateWithError($updatedPresetData) ?? [null];
$updatedPreset = $updatedPresetArray[0];
$this->terminateWithSuccess($updatedPreset);
}
public function deleteTabulatorPreset()
{
$presetId = $this->input->post("presetId");
if (!$presetId) {
$this->terminateWithError($this->p->t("global", "invalid_params_err_msg"), "general", 400);
}
$presetData = $this->TabulatorPresetModel->getTabulatorPreset($presetId);
$presetArray = $this->getDataOrTerminateWithError($presetData) ?? [null];
$preset = $presetArray[0];
if (!$preset) {
$this->terminateWithError($this->p->t("tabulator_presets", "preset_not_found_err_msg"), "general", 404);
}
$uid = getAuthUID();
if ($preset->benutzer_uid !== $uid) {
$this->terminateWithError($this->p->t("tabulator_presets", "preset_not_own_delete_err_msg"), "general", 403);
}
$presetDeletionResult = $this->TabulatorPresetModel->deleteTabulatorPreset($presetId);
if (isError($presetDeletionResult)) {
$this->terminateWithError($presetDeletionResult->retval);
}
$this->terminateWithSuccess();
}
}
@@ -0,0 +1,83 @@
<?php
class Tabulator_preset_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'public.tbl_tabulator_presets';
$this->pk = ['preset_id'];
$this->hasSequence = true;
$this->load->model('person/Benutzer_model', 'BenutzerModel');
}
public function getTabulatorPreset($presetId)
{
$query = "
SELECT *
FROM public.tbl_tabulator_presets
WHERE preset_id = ?
LIMIT 1
";
return $this->execQuery($query, array($presetId));
}
public function getTabulatorPresetsByUserAndTable($uid, $tableName)
{
$query = '
SELECT *
FROM public.tbl_tabulator_presets
WHERE benutzer_uid = ?
AND table_name = ?
ORDER BY preset_id ASC
';
return $this->execQuery($query, array($uid, $tableName));
}
public function createTabulatorPreset($uid, $tableName, $presetName, $preset)
{
$presetCreationResult = $this->insert([
"benutzer_uid" => $uid,
"table_name" => $tableName,
"preset_name" => $presetName,
"preset_json" => json_encode($preset),
]);
if (isError($presetCreationResult))
{
return error('Something went wrong during preset creation!');
}
return success($presetCreationResult->retval);
}
public function updateTabulatorPreset($presetId, $preset)
{
$presetUpdateResult = $this->update(["preset_id" => $presetId], ["preset_json" => json_encode($preset)]);
if (isError($presetUpdateResult))
{
return error('Something went wrong during preset update!');
}
return success($presetUpdateResult->retval);
}
public function deleteTabulatorPreset($presetId)
{
$presetDeletionReseult = $this->delete(["preset_id" => $presetId]);
if (isError($presetDeletionReseult))
{
return error('Something went wrong during preset deletion!');
}
return success($presetDeletionReseult->retval);
}
}
+58
View File
@@ -0,0 +1,58 @@
/**
* 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 {
getTabulatorPresets(tableName) {
return {
method: "get",
url:
"/api/frontend/v1/TabulatorPresets/getTabulatorPresets?tableName=" +
tableName,
};
},
createTabulatorPreset(tableName, presetName, preset) {
return {
method: "post",
url: "/api/frontend/v1/TabulatorPresets/createTabulatorPreset",
params: {
tableName,
presetName,
preset,
},
};
},
updateTabulatorPreset(presetId, preset) {
return {
method: "post",
url: "/api/frontend/v1/TabulatorPresets/updateTabulatorPreset",
params: {
presetId,
preset,
},
};
},
deleteTabulatorPreset(tabulatorPresetId) {
return {
method: "post",
url:
"/api/frontend/v1/TabulatorPresets/deleteTabulatorPreset",
params: {
presetId: tabulatorPresetId,
},
};
},
};
@@ -469,6 +469,7 @@ export default {
:tabulator-options="betriebsmittel_table_options"
tableOnly
:sideMenu="false"
:isUsingPresets="true"
/>
</div>
</div>
@@ -43,6 +43,7 @@ export default {
maxWidth: 40,
headerClick: this.collapseFunction,
visible: true,
responsive: 0,
},
{
title: "placeholder",
@@ -51,6 +52,7 @@ export default {
headerFilter: true,
minWidth: 200,
visible: true,
responsive: 0,
},
{
title: "placeholder",
@@ -59,6 +61,7 @@ export default {
headerFilter: true,
minWidth: 200,
visible: true,
responsive: 1,
},
{
title: "placeholder",
@@ -71,6 +74,7 @@ export default {
visible: true,
formatter: "datetime",
formatterParams: this.datetimeFormatterParams(),
responsive: 4,
},
{
title: "placeholder",
@@ -83,6 +87,7 @@ export default {
visible: true,
formatter: "datetime",
formatterParams: this.datetimeFormatterParams(),
responsive: 3,
},
{
title: "placeholder",
@@ -91,6 +96,7 @@ export default {
headerFilter: true,
minWidth: 200,
visible: true,
responsive: 2,
},
],
},
@@ -176,7 +176,6 @@ export const Profil = {
this.data = data.profil_data.data;
this.calendarSyncUrls = data.calendar_sync_urls ?? [];
this.authPermissions = data.permissions;
console.log(data.profil_data);
},
zustellAdressenCount() {
if (!this.data || !this.data.adressen) {
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,565 @@
/**
* Copyright (C) 2022 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/>.
*/
import ApiTabulatorPresets from "../../../api/factory/tabulatorPresets.js";
/**
*
*/
export default {
name: "TablePresets",
props: {
presetsId: { type: String },
tabulator: { type: Object },
generalPresets: { type: Array, default: [] },
},
emits: ["tablePresetApplied"],
data: function () {
return {
customUserPresets: [],
presetInfo: null,
newPreset: null,
newPresetName: "",
};
},
computed: {
allPresets() {
return this.$props.generalPresets.concat(this.customUserPresets);
},
presetInfoFormattedStrings() {
if (!this.presetInfo) return null;
const columnTitles = this.getColumnTitles();
let displayedColumns =
this.presetInfo.displayedColumns?.map(
(columnField) => columnTitles[columnField] ?? "",
) ?? [];
displayedColumns = displayedColumns.filter(
(string) => string.length,
);
const displayedColumnsString = displayedColumns.length
? this.$p.t("tabulator_presets/displayed_columns") +
" (" +
this.$p.t("tabulator_presets/in_order") +
"): " +
displayedColumns.join(", ")
: "";
let headerFilters = this.presetInfo.headerFilters
? Object.keys(this.presetInfo.headerFilters).map((column) =>
columnTitles[column]
? columnTitles[column] +
'["' +
this.presetInfo.headerFilters[column] +
'"]'
: "",
)
: [];
headerFilters = headerFilters.filter((string) => string.length);
const headerFiltersString = headerFilters.length
? this.$p.t("tabulator_presets/column_header_filters") +
": " +
headerFilters.join(", ")
: "";
const sortColumn = this.presetInfo.sort?.column
? columnTitles[this.presetInfo.sort.column]
: null;
const sortDirection =
this.presetInfo.sort?.direction === "desc"
? this.$p.t("tabulator_presets/desc")
: this.$p.t("tabulator_presets/asc");
const sortString = sortColumn
? this.$p.t("tabulator_presets/sort") +
": " +
sortColumn +
" (" +
sortDirection +
")"
: "";
return {
displayedColumns: displayedColumnsString,
headerFilters: headerFiltersString,
sort: sortString,
};
},
newPresetFormattedStrings() {
if (!this.newPreset) return null;
const columnTitles = this.getColumnTitles();
const displayedColumns = this.newPreset.displayedColumns.map(
(columnField) => columnTitles[columnField],
);
const displayedColumnsString = displayedColumns.length
? this.$p.t("tabulator_presets/displayed_columns") +
" (" +
this.$p.t("tabulator_presets/in_order") +
"): " +
displayedColumns.join(", ")
: "";
const headerFilters = Object.keys(this.newPreset.headerFilters).map(
(column) =>
columnTitles[column] +
'["' +
this.newPreset.headerFilters[column] +
'"]',
);
const headerFiltersString = headerFilters.length
? this.$p.t("tabulator_presets/column_header_filters") +
": " +
headerFilters.join(", ")
: "";
const sortColumn = this.newPreset.sort?.column
? columnTitles[this.newPreset.sort.column]
: null;
const sortDirection =
this.newPreset.sort?.direction === "desc"
? this.$p.t("tabulator_presets/desc")
: this.$p.t("tabulator_presets/asc");
const sortString = sortColumn
? this.$p.t("tabulator_presets/sort") +
": " +
sortColumn +
" (" +
sortDirection +
")"
: "";
return {
displayedColumns: displayedColumnsString,
headerFilters: headerFiltersString,
sort: sortString,
};
},
},
watch: {
allPresets: {
handler() {
this.hidePresetInfo();
this.hideNewPresetForm();
},
deep: true,
},
},
methods: {
async showPresetInfo(preset) {
let shouldUpdatePresetInfo;
if (!this.presetInfo) {
shouldUpdatePresetInfo = true;
} else if (preset.id) {
shouldUpdatePresetInfo = preset.id !== this.presetInfo.id;
} else {
shouldUpdatePresetInfo = preset.name !== this.presetInfo.name;
}
if (shouldUpdatePresetInfo) {
this.presetInfo = {...preset};
}
this.hideNewPresetForm();
await this.$nextTick();
if (
!this.$refs.presetInfoCollapsible
.getAttribute("class")
.split(" ")
.includes("show")
) {
this.togglePresetInfoCollapsible();
}
},
async hidePresetInfo() {
if (
this.$refs.presetInfoCollapsible
.getAttribute("class")
.split(" ")
.includes("show")
) {
this.togglePresetInfoCollapsible();
}
await this.$nextTick();
this.presetInfo = null;
},
togglePresetInfoCollapsible() {
new bootstrap.Collapse(
"#presetInfoCollapsible_" + this.$props.presetsId,
{ toggle: true },
);
},
async showNewPresetForm() {
this.generateNewPreset();
this.hidePresetInfo();
await this.$nextTick();
if (
!this.$refs.newPresetFormCollapsible
.getAttribute("class")
.split(" ")
.includes("show")
) {
this.toggleNewPresetFormCollapsible();
}
},
generateNewPreset() {
const columns = this.tabulator.columnManager.columns.filter(
(column) => column.field !== "collapse",
);
const displayedColumns = columns
.filter((column) => column.visible)
.map((column) => column.field);
const unparsedHeaderFilters =
this.tabulator.modules.filter.headerFilters;
const headerFilterFieldValuePairs = Object.entries(
unparsedHeaderFilters,
).map(([columnField, filter]) => [columnField, filter.value]);
const headerFiltersFieldValuePairsExcludingHiddenColumns =
headerFilterFieldValuePairs.filter(
([columnField, filterValue]) => {
return displayedColumns.includes(columnField);
},
);
const headerFilters = Object.fromEntries(
headerFiltersFieldValuePairsExcludingHiddenColumns,
);
const activeSort = this.tabulator.getSorters()[0];
const sort = activeSort
? {
column: activeSort.field,
direction: activeSort.dir,
}
: null;
this.newPreset = {
displayedColumns,
headerFilters,
sort,
};
},
hideNewPresetForm() {
if (
this.$refs.newPresetFormCollapsible
.getAttribute("class")
.split(" ")
.includes("show")
) {
this.toggleNewPresetFormCollapsible();
}
},
toggleNewPresetFormCollapsible() {
this.newPresetName = "";
new bootstrap.Collapse(
"#newPresetFormCollapsible_" + this.$props.presetsId,
{ toggle: true },
);
},
async createPreset() {
if (!this.newPresetName?.length) return;
const presetCreationResponse = await this.$api.call(
ApiTabulatorPresets.createTabulatorPreset(
this.$props.presetsId,
this.newPresetName,
this.newPreset,
),
);
if (presetCreationResponse.meta.status === "success") {
this.fetchCustomUserTabulatorPresets();
this.hideNewPresetForm();
}
},
async deletePreset(preset) {
if (!preset.id) return;
if (
window.confirm(
this.$p
.t("tabulator_presets/preset_deletion_confirmation")
.replace("(((presetName)))", preset.name),
)
) {
const presetDeletionResponse = await this.$api.call(
ApiTabulatorPresets.deleteTabulatorPreset(preset.id),
);
if (presetDeletionResponse.meta.status === "success") {
this.fetchCustomUserTabulatorPresets();
}
}
},
syncPresetWithConfig(preset) {
const validColumns = this.$props.tabulator.getColumnDefinitions();
let isUpdateNecessary = false;
if (preset.displayedColumns) {
const initialNumberOfDisplayedColumns =
preset.displayedColumns.length;
preset.displayedColumns = preset.displayedColumns.filter(
(columnField) => {
return validColumns.some(
(column) => column.field === columnField,
);
},
);
const updatedNumberOfDisplayedColumns =
preset.displayedColumns.length;
isUpdateNecessary =
updatedNumberOfDisplayedColumns <
initialNumberOfDisplayedColumns;
}
if (preset.headerFilters) {
const initialNumberOfHeaderFilters = Object.keys(
preset.headerFilters,
).length;
Object.keys(preset.headerFilters).forEach((columnField) => {
if (
!validColumns.some(
(column) =>
column.field === columnField &&
column.headerFilter,
) ||
!preset.displayedColumns.includes(columnField)
) {
delete preset.headerFilters[columnField];
}
});
const updatedNumberOfHeaderFilters = Object.keys(
preset.headerFilters,
).length;
isUpdateNecessary =
isUpdateNecessary ||
updatedNumberOfHeaderFilters < initialNumberOfHeaderFilters;
}
if (preset.sort) {
if (
!validColumns.some(
(column) =>
column.field === preset.sort.column &&
column.headerSort,
)
) {
preset.sort = null;
isUpdateNecessary = true;
}
}
if (isUpdateNecessary) {
this.updatePreset(preset);
}
return preset;
},
async updatePreset(preset) {
if (!preset.id) return;
const tabulatorPresetUpdateResponse = await this.$api.call(
ApiTabulatorPresets.updateTabulatorPreset(preset.id, {
displayedColumns: preset.displayedColumns,
headerFilters: preset.headerFilters,
sort: preset.sort,
}),
);
if (tabulatorPresetUpdateResponse.meta.status === "success") {
this.fetchCustomUserTabulatorPresets();
}
},
async applyPreset(preset) {
preset = this.syncPresetWithConfig(preset);
window.localStorage["tabulatorPreset-" + this.$props.presetsId] =
preset.id ? "custom:" + preset.id : "general:" + preset.name;
let columns = this.$props.tabulator
.getColumnDefinitions()
.filter((column) => column.field !== "collapse");
columns.forEach((column) =>
this.$props.tabulator.hideColumn(column.field),
);
preset.displayedColumns?.forEach(
(columnField, index, displayedColumns) => {
this.$props.tabulator.showColumn(columnField);
if (index === 0) return;
this.$props.tabulator.moveColumn(
columnField,
displayedColumns[index - 1],
true,
);
},
);
this.$props.tabulator.clearHeaderFilter();
if (preset.headerFilters) {
Object.entries(preset.headerFilters).forEach(
([columnField, filterValue]) => {
this.$props.tabulator.setHeaderFilterValue(
columnField,
filterValue,
);
},
);
}
this.$props.tabulator.clearSort();
if (preset.sort?.column) {
this.$props.tabulator.setSort(
preset.sort.column,
preset.sort.direction ?? "asc",
);
}
this.$emit("tablePresetApplied", { preset });
},
async fetchCustomUserTabulatorPresets() {
let tabulatorPresetsResponse = await this.$api.call(
ApiTabulatorPresets.getTabulatorPresets(this.$props.presetsId),
);
if (tabulatorPresetsResponse.meta.status === "success") {
this.customUserPresets = tabulatorPresetsResponse.data.map(
(presetInfo) => {
return {
id: presetInfo.preset_id,
name: presetInfo.preset_name,
...JSON.parse(presetInfo.preset_json),
};
},
);
}
},
getColumnTitles() {
const columnLocalizations =
this.$props.tabulator.getLocale() !== "default"
? this.$props.tabulator.getLang().columns
: {};
const columnFieldTitlePairs = this.$props.tabulator
.getColumnDefinitions()
.map((column) => [
column.field,
columnLocalizations[column.field] ?? column.title,
]);
return Object.fromEntries(columnFieldTitlePairs);
},
applyStoredPreset() {
let storedPreset =
window.localStorage["tabulatorPreset-" + this.$props.presetsId];
if (!storedPreset) return;
let preset = null;
if (storedPreset.search("custom:") === 0) {
const customPresetId = parseInt(storedPreset.slice(7));
preset = this.customUserPresets.find(
(customPreset) => customPreset.id === customPresetId,
);
} else if (storedPreset.search("general:") === 0) {
const generalPresetName = storedPreset.slice(8);
preset = this.$props.generalPresets.find(
(generalPreset) => generalPreset.name === generalPresetName,
);
}
if (!preset) return;
this.applyPreset(preset);
},
},
async created() {
await this.$p.loadCategory(["tabulator_presets", "global"]);
await this.fetchCustomUserTabulatorPresets();
this.applyStoredPreset();
},
template: /*html*/ `
<div>
<div class="card">
<div class="card-header">{{ $p.t("tabulator_presets/table_presets") }}</div>
<div class="card-body d-flex flex-column">
<div class="d-flex flex-row gap-1 justify-content-start flex-wrap">
<div v-for="preset in allPresets" class="d-flex flex-column gap-1 mb-2">
<div @click="applyPreset(preset)" class="btn btn-dark py-1 px-2">{{ preset.name }}</div>
<div class="d-flex flex-row justify-content-center">
<div class="d-flex flex-row gap-2 px-3">
<span @click="showPresetInfo(preset)" type="button" class="fa-solid fa-circle-info"></span>
<span v-if="preset.id" @click="deletePreset(preset)" type="button" class="fa-solid fa-trash-can"></span>
</div>
</div>
</div>
<div>
<div @click="showNewPresetForm()" class="btn btn-dark py-1 px-2 d-flex flex-row align-items-center gap-1">
<span class="fa-solid fa-plus"></span>
<span class="flex-nowrap">{{ $p.t("tabulator_presets/save_current_config") }}</span>
</div>
</div>
</div>
<div v-collapse-auto-close :id="'presetInfoCollapsible_' + $props.presetsId" ref="presetInfoCollapsible" class="collapse">
<div class="d-flex flex-column">
<hr />
<div class="d-flex flex-column gap-2">
<div class="w-100 d-flex flex-row justify-content-between">
<span class="fw-bold">{{ presetInfo?.name }}</span>
<span @click="hidePresetInfo()" type="button" class="fa-solid fa-xmark"></span>
</div>
<span v-if="presetInfoFormattedStrings?.displayedColumns.length">{{ presetInfoFormattedStrings.displayedColumns }}</span>
<span v-if="presetInfoFormattedStrings?.headerFilters.length">{{ presetInfoFormattedStrings.headerFilters }}</span>
<span v-if="presetInfoFormattedStrings?.sort.length">{{ presetInfoFormattedStrings.sort }}</span>
</div>
</div>
</div>
<div v-collapse-auto-close :id="'newPresetFormCollapsible_' + $props.presetsId" ref="newPresetFormCollapsible" class="collapse">
<div class="d-flex flex-column">
<hr />
<div class="d-flex flex-column gap-2">
<div class="w-100 d-flex flex-row justify-content-between">
<span class="fw-bold">{{ $p.t("tabulator_presets/save_current_config") }}</span>
<span @click="hideNewPresetForm()" type="button" class="fa-solid fa-xmark"></span>
</div>
<span v-if="newPresetFormattedStrings?.displayedColumns.length">{{ newPresetFormattedStrings.displayedColumns }}</span>
<span v-if="newPresetFormattedStrings?.headerFilters.length">{{ newPresetFormattedStrings.headerFilters }}</span>
<span v-if="newPresetFormattedStrings?.sort.length">{{ newPresetFormattedStrings.sort }}</span>
<div class="d-flex flex-row justify-content-center align-items-center gap-2">
<input v-model="newPresetName" :placeholder="$p.t('tabulator_presets/preset_name')" />
<div
@click="createPreset()"
:class="{'opacity-50 pe-none': !newPresetName?.length}"
class="btn btn-dark py-1 px-2"
>
{{ $p.t('global/speichern') }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`,
};
@@ -9,9 +9,13 @@ export default class ResponsiveLayoutExtensionModule extends ResponsiveLayout {
}
generateCollapsedRowData(row) {
let result = super.generateCollapsedRowData(row)
const isLocalizationEnabled = this.table.options.locale && this.table.options.locale !== "default";
const columnHeadingTranslations = this.table?.getLang()?.columns;
let result = super.generateCollapsedRowData(row);
const isLocalizationEnabled =
this.table.options.locale &&
this.table.options.locale !== "default";
const columnHeadingTranslations = this.table?.initialized
? this.table.getLang()?.columns
: null;
if (isLocalizationEnabled && columnHeadingTranslations) {
result = result.map((column) => {
+2
View File
@@ -99,6 +99,7 @@ require_once('dbupdate_3.4/68957_dashboard_bookmark_neue_Spalte_sort.php');
require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
require_once('dbupdate_3.4/75959_StudVw_Automatische_Tags.php');
require_once('dbupdate_3.4/76160_lvv_favorites.php');
require_once('dbupdate_3.4/77080_tabulator_presets_table.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -396,6 +397,7 @@ $tabellen=array(
"public.tbl_studiengangstyp" => array("typ","bezeichnung","beschreibung","bezeichnung_mehrsprachig"),
"public.tbl_studienjahr" => array("studienjahr_kurzbz","bezeichnung"),
"public.tbl_studiensemester" => array("studiensemester_kurzbz","bezeichnung","start","ende","studienjahr_kurzbz","ext_id","beschreibung","onlinebewerbung"),
"public.tbl_tabulator_presets" => array("preset_id", "benutzer_uid", "table_name", "preset_json"),
"public.tbl_tag" => array("tag"),
"public.tbl_variable" => array("name","uid","wert"),
"public.tbl_variablenname" => array("name","defaultwert"),
@@ -0,0 +1,55 @@
<?php
if (!defined('DB_NAME'))
exit('No direct script access allowed');
if (!$result = @$db->db_query("SELECT 1 FROM public.tbl_tabulator_presets LIMIT 1")) {
$qry = "CREATE TABLE public.tbl_tabulator_presets (
preset_id int NOT NULL,
benutzer_uid varchar(33) NOT NULL,
table_name varchar(255) NOT NULL,
preset_name varchar(255) NOT NULL,
preset_json varchar(10000) NOT NULL,
CONSTRAINT tbl_tabulator_presets_preset_id_pk PRIMARY KEY(preset_id),
CONSTRAINT tbl_tabulator_presets_benutzer_uid_fk FOREIGN KEY (benutzer_uid) REFERENCES public.tbl_benutzer(uid)
);";
if (!$db->db_query($qry))
echo '<strong>public.tbl_tabulator_presets: ' . $db->db_last_error() . '</strong><br>';
else
echo '<br>public.tbl_tabulator_presets: table created';
$db->db_query('CREATE SEQUENCE IF NOT EXISTS public.seq_tbl_tabulator_presets_preset_id
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;');
$db->db_query("ALTER TABLE public.tbl_tabulator_presets ALTER COLUMN preset_id SET DEFAULT nextval('public.seq_tbl_tabulator_presets_preset_id');");
$qry = 'GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE public.tbl_tabulator_presets TO web;';
if (!$db->db_query($qry))
echo '<strong>public.tbl_tabulator_presets: ' . $db->db_last_error() . '</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on public.tbl_tabulator_presets';
$qry = 'GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE public.tbl_tabulator_presets TO vilesci;';
if (!$db->db_query($qry))
echo '<strong>public.tbl_tabulator_presets: ' . $db->db_last_error() . '</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on public.tbl_tabulator_presets';
$qry = 'GRANT USAGE ON public.seq_tbl_tabulator_presets_preset_id TO web;';
if (!$db->db_query($qry))
echo '<strong>public.tbl_tabulator_presets: ' . $db->db_last_error() . '</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on public.tbl_tabulator_presets';
$qry = 'GRANT USAGE ON public.seq_tbl_tabulator_presets_preset_id TO vilesci;';
if (!$db->db_query($qry))
echo '<strong>public.tbl_tabulator_presets: ' . $db->db_last_error() . '</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on public.tbl_tabulator_presets';
}
+362
View File
@@ -61721,6 +61721,368 @@ I have been informed that I am under no obligation to consent to the transmissio
)
),
//End Finetuning StudVW
// tabulator presets section phrases START
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'displayed_columns',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Angezeigte Spalten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Displayed columns',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'in_order',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'in Reihenfolge',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'in order',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'column_header_filters',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Spalten Header Filter',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Column header filters',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'asc',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'aufsteigend',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'ascending',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'desc',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'absteigend',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'descending',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'sort',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sortierung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Sort',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_deletion_confirmation',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sind Sie sicher, dass Sie die Voreinstellung (((presetName))) löschen möchten?',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Are you sure you want to delete preset (((presetName)))?',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'table_presets',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Tabelle Voreinstellungen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Table presets',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'save_current_config',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Aktuelle Einstellung speichern',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Save current configuration',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_name',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Voreinstellung Name',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Preset name',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'max_presets_count_err_msg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Maximale Anzahl an Voreinstellungen erreicht!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Maximum number of presets reached!',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_name_duplicate_err_msg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Doppelte Voreinstellunge Namen sind nicht zulässig!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Preset name duplicate not allowed!',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_not_found_err_msg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Voreinstellung nicht gefunden!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Preset not found!',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_not_own_update_err_msg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sie dürfen diese Voreinstellung nicht ändern!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'You are not allowed to update this preset!',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_not_own_delete_err_msg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sie dürfen diese Voreinstellung nicht löschen!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'You are not allowed to delete this preset!',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_creation_generic_err_msg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bei der Erstellung der Voreinstellung ist ein Fehler aufgetreten!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Something went wrong during preset creation!',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_update_generic_err_msg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bei der Voreinstellungsänderung ist ein Fehler aufgetreten!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Something went wrong during preset update!',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'tabulator_presets',
'phrase' => 'preset_deletion_generic_err_msg',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Beim Löschen der Voreinstellung ist ein Fehler aufgetreten!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Something went wrong during preset deletion!',
'description' => '',
'insertvon' => 'system'
)
)
),
// tabulator presets section phrases END
);