implemented profile calendar sync section

This commit is contained in:
adisposkofh
2026-04-14 14:08:16 +02:00
parent 7daaf79fcc
commit ebafc4576f
6 changed files with 329 additions and 73 deletions
+47 -22
View File
@@ -55,15 +55,16 @@ class Profil extends Auth_Controller
*/
public function index()
{
$this->load->library('ProfilLib');
$profil_data = $this->profillib->getView(getAuthUID());
$profil_data = hasData($profil_data) ? getData($profil_data) : null;
$viewData = array(
'editable'=>true,
'editable' => true,
'profil_data' => $profil_data,
'calendarSyncUrls' => $this->getCalendarSyncUrlData(),
);
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilIndex']);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'profilIndex']);
}
/**
@@ -76,17 +77,17 @@ class Profil extends Auth_Controller
$this->load->library('ProfilLib');
$profil_data = $this->profillib->getView($uid);
$profil_data = hasData($profil_data) ? getData($profil_data) : null;
$viewData = array (
$viewData = array(
'uid' => $uid,
'profil_data'=>$profil_data,
'profil_data' => $profil_data,
'permissions' => [
'basis/other_lv_plan' => $this->permissionlib->isBerechtigt(('basis/other_lv_plan')),
]
);
if($uid == getAuthUID()){
if ($uid == getAuthUID()) {
$viewData['editable'] = true;
}
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilViewUid']);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'profilViewUid']);
}
/**
@@ -265,23 +266,23 @@ class Profil extends Auth_Controller
$this->GemeindeModel->addDistinct();
$this->GemeindeModel->addSelect(["name"]);
if ($nation == "A") {
if (isset($zip) && $zip > 999 && $zip < 32000) {
if (isset($zip) && $zip > 999 && $zip < 32000) {
$gemeinde_res = $this->GemeindeModel->loadWhere(['plz' => $zip]);
if (isError($gemeinde_res)) {
show_error("error while trying to query bis.tbl_gemeinde");
}
$gemeinde_res = hasData($gemeinde_res) ? getData($gemeinde_res) : null;
$gemeinde_res = array_map(function ($obj) {
return $obj->name;
}, $gemeinde_res);
echo json_encode($gemeinde_res);
} else {
echo json_encode(error("ortschaftskennziffer code was not valid"));
$gemeinde_res = $this->GemeindeModel->loadWhere(['plz' => $zip]);
if (isError($gemeinde_res)) {
show_error("error while trying to query bis.tbl_gemeinde");
}
$gemeinde_res = hasData($gemeinde_res) ? getData($gemeinde_res) : null;
$gemeinde_res = array_map(function ($obj) {
return $obj->name;
}, $gemeinde_res);
echo json_encode($gemeinde_res);
} else {
echo json_encode(error("ortschaftskennziffer code was not valid"));
}
} else {
echo json_encode(error("Nation was not 'A' (Austria)"));
echo json_encode(error("Nation was not 'A' (Austria)"));
}
}
@@ -754,5 +755,29 @@ class Profil extends Auth_Controller
return $zutrittskarte_ausgegebenam;
}
/**
* gets the identifier, phrase, and url for each calendar sync option
* @access private
* @return array array of arrays, where each child array is a sync option
*/
private function getCalendarSyncUrlData()
{
return [
[
"identifier" => "cal_dav",
"labelPhrase" => "profil/calendar_sync_cal_dav",
"url" => APP_ROOT . "webdav/lvplan.php/calendars/" . $this->uid . "/LVPlan-" . $this->uid,
],
[
"identifier" => "cal_dav_principal",
"labelPhrase" => "profil/calendar_sync_cal_dav_principal",
"url" => APP_ROOT . "webdav/lvplan.php/principals/" . $this->uid,
],
[
"identifier" => "i_cal",
"labelPhrase" => "profil/calendar_sync_i_cal",
"url" => APP_ROOT . "webdav/google.php?cal=" . encryptData($this->uid, LVPLAN_CYPHER_KEY) . "&" . microtime(true),
],
];
}
}
@@ -9,6 +9,7 @@ import QuickLinks from "./ProfilComponents/QuickLinks.js";
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
import RoleInformation from "./ProfilComponents/RoleInformation.js";
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
import CalendarSync from "./ProfilComponents/CalendarSync.js";
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
import { dateFilter } from '../../../tabulator/filters/Dates.js';
@@ -26,6 +27,7 @@ export default {
ProfilEmails,
RoleInformation,
ProfilInformation,
CalendarSync,
},
inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"],
@@ -160,6 +162,7 @@ export default {
props: {
data: Object,
editData: Object,
calendarSyncUrls: Array,
},
methods: {
@@ -313,7 +316,6 @@ export default {
});
//? sorts the profil Updates: pending -> accepted -> rejected
this.data.profilUpdates?.sort(this.sortProfilUpdates);
},
watch: {
'data.funktionen'(newVal) {
@@ -489,12 +491,17 @@ export default {
<ausweis-status :data="data.zutrittsdatum"></ausweis-status>
</div>
</div>
<div class="row">
<div class="row mb-3">
<div class="col">
<!-- MAILVERTEILER -->
<mailverteiler :data="data?.mailverteiler" :title="$p.t('profil','mailverteiler')"></mailverteiler>
</div>
</div>
<div class="row">
<div class="col">
<calendar-sync :uid="$props.data.username" :calendarSyncUrls="$props.calendarSyncUrls"></calendar-sync>
</div>
</div>
</div>
</div>
</div>
+68 -47
View File
@@ -4,8 +4,8 @@ import ViewStudentProfil from "./StudentViewProfil.js";
import ViewMitarbeiterProfil from "./MitarbeiterViewProfil.js";
import Loading from "../../Loader.js";
import ApiProfil from '../../../api/factory/profil.js';
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
import ApiProfil from "../../../api/factory/profil.js";
import ApiProfilUpdate from "../../../api/factory/profilUpdate.js";
Vue.$collapseFormatter = function (data) {
//data - an array of objects containing the column title and value for each cell
@@ -35,7 +35,7 @@ Vue.$collapseFormatter = function (data) {
};
export const Profil = {
name: 'Profil',
name: "Profil",
components: {
StudentProfil,
MitarbeiterProfil,
@@ -46,11 +46,11 @@ export const Profil = {
props: {
uid: {
type: String,
required:false,
required: false,
},
viewData: {
type: Object,
}
},
},
data() {
return {
@@ -67,12 +67,12 @@ export const Profil = {
},
provide() {
return {
isEditable: Vue.computed(()=>this.isEditable),
isEditable: Vue.computed(() => this.isEditable),
profilUpdateStates: Vue.computed(() =>
this.profilUpdateStates ? this.profilUpdateStates : false
this.profilUpdateStates ? this.profilUpdateStates : false,
),
profilUpdateTopic: Vue.computed(() =>
this.profilUpdateTopic ? this.profilUpdateTopic : false
this.profilUpdateTopic ? this.profilUpdateTopic : false,
),
setLoading: (newValue) => {
this.loading = newValue;
@@ -130,8 +130,12 @@ export const Profil = {
//? if they have the same status the insert date is used for ordering
if (ele1.status === ele2.status) {
result =
new Date(ele2.insertamum.split(".").reverse().join("-")) -
new Date(ele1.insertamum.split(".").reverse().join("-"));
new Date(
ele2.insertamum.split(".").reverse().join("-"),
) -
new Date(
ele1.insertamum.split(".").reverse().join("-"),
);
}
return result;
},
@@ -157,11 +161,11 @@ export const Profil = {
.catch((error) => {
console.error(error);
});
this.$api
.call(ApiProfil.profilViewData(this.$route.params.uid??null))
.then((response) => response.data).then(data=>{
.call(ApiProfil.profilViewData(this.$route.params.uid ?? null))
.then((response) => response.data)
.then((data) => {
this.view = data?.profil_data.view;
this.data = data?.profil_data.data;
this.isEditable = data?.editable ?? false;
@@ -169,8 +173,6 @@ export const Profil = {
.catch((error) => {
console.error(error);
});
},
zustellAdressenCount() {
if (!this.data || !this.data.adressen) {
@@ -186,7 +188,7 @@ export const Profil = {
})
.map((adresse) => {
return adresse.requested_change.adresse_id;
})
}),
);
}
@@ -197,8 +199,9 @@ export const Profil = {
.every((adresse) =>
this.data.profilUpdates.some(
(update) =>
update.requested_change.adresse_id == adresse.adresse_id
)
update.requested_change.adresse_id ==
adresse.adresse_id,
),
)
) {
adressenArray = adressenArray.concat(
@@ -208,12 +211,11 @@ export const Profil = {
})
.map((adr) => {
return adr.adresse_id;
})
}),
);
}
return [...new Set(adressenArray)];
},
zustellKontakteCount() {
if (!this.data || !this.data.kontakte) {
@@ -226,14 +228,17 @@ export const Profil = {
kontakteArray = kontakteArray.concat(
this.data.profilUpdates
.filter((update) => {
return update.status === 'Pending' && update.requested_change.zustellung;
return (
update.status === "Pending" &&
update.requested_change.zustellung
);
})
.map((kontant) => {
return {
kontakt_id: kontant.requested_change.kontakt_id,
kontakttyp: kontant.requested_change.kontakttyp
};
})
kontakt_id: kontant.requested_change.kontakt_id,
kontakttyp: kontant.requested_change.kontakttyp,
};
}),
);
}
@@ -244,8 +249,10 @@ export const Profil = {
.every((kontakt) =>
this.data.profilUpdates.some(
(update) =>
update.status === 'Pending' && update.requested_change.kontakt_id == kontakt.kontakt_id
)
update.status === "Pending" &&
update.requested_change.kontakt_id ==
kontakt.kontakt_id,
),
)
) {
kontakteArray = kontakteArray.concat(
@@ -255,10 +262,10 @@ export const Profil = {
})
.map((kon) => {
return {
kontakt_id: kon.kontakt_id,
kontakttyp: kon.kontakttyp
};
})
kontakt_id: kon.kontakt_id,
kontakttyp: kon.kontakttyp,
};
}),
);
}
@@ -266,7 +273,6 @@ export const Profil = {
},
},
computed: {
filteredEditData() {
if (!this.data) {
return;
@@ -330,8 +336,12 @@ export const Profil = {
// excludes all contacts that are already used in pending profil update requests
return !this.data.profilUpdates?.some(
(update) =>
update.status === this.profilUpdateStates["Pending"] &&
update.requested_change?.kontakt_id === item.kontakt_id
update.status ===
this.profilUpdateStates[
"Pending"
] &&
update.requested_change?.kontakt_id ===
item.kontakt_id,
);
})
.map((kontakt) => {
@@ -347,12 +357,18 @@ export const Profil = {
topic: this.profilUpdateTopic?.["Private Adressen"],
data: this.data.adressen
?.filter((item) => {
return !this.data.profilUpdates?.some((update) => {
return (
update.status === this.profilUpdateStates["Pending"] &&
update.requested_change?.adresse_id == item.adresse_id
);
});
return !this.data.profilUpdates?.some(
(update) => {
return (
update.status ===
this.profilUpdateStates[
"Pending"
] &&
update.requested_change
?.adresse_id == item.adresse_id
);
},
);
})
.map((adresse) => {
return {
@@ -374,9 +390,9 @@ export const Profil = {
this.$refs.loadingModalRef.hide();
}
},
uid (newVal, oldVal) {
this.load()
}
uid(newVal, oldVal) {
this.load();
},
},
created() {
this.load();
@@ -388,9 +404,14 @@ export const Profil = {
</div>
<div v-else>
<loading ref="loadingModalRef" :timeout="0"></loading>
<component :is="view" :data="data" :permissions="$props.viewData.permissions" :editData="filteredEditData" ></component>
<component
:is="view"
:data="data"
:editData="filteredEditData"
:permissions="$props.viewData.permissions"
:calendarSyncUrls="$props.viewData.calendarSyncUrls"></component>
</div>
</div>`,
}
};
export default Profil
export default Profil;
@@ -0,0 +1,52 @@
export default {
name: "CalendarSync",
props: { uid: String, calendarSyncUrls: Array },
data() {
return {
syncInstructionsUrlWithoutParam:
FHC_JS_DATA_STORAGE_OBJECT.app_root +
"cms/content.php?content_id=",
};
},
methods: {
copyUrlToClipboard(url) {
navigator.clipboard.writeText(url);
this.$fhcAlert.alertSuccess(
this.$p.t("profil/calendar_sync_clipboard_copy_confirmation"),
);
},
},
template: `
<div class="card">
<div class="card-header">
{{ $p.t("profil/calendar_sync") }}
</div>
<div class="card-body">
<div class="d-flex flex-column gap-3">
<span>
<a
target="_blank"
:href="syncInstructionsUrlWithoutParam + $p.t('DMS-Link/lvplanSyncFAQ')"
class="fhc-link-color"
>
{{ $p.t("profil/calendar_sync_instructions") }}
</a>
<i class="fa-solid fa-up-right-from-square ms-2"></i>
</span>
<ul>
<li v-for="syncUrl in $props.calendarSyncUrls" :key="syncUrl.identifier">
<a
@click.prevent="copyUrlToClipboard(syncUrl.url)"
@contextmenu.prevent="copyUrlToClipboard(syncUrl.url)"
href="#"
class="fhc-link-color"
>
{{ $p.t(syncUrl.labelPhrase) }}
</a>
<i class="fa-regular fa-copy ms-2"></i>
</li>
</ul>
</div>
</div>
</div>`,
};
@@ -9,6 +9,7 @@ import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
import FetchProfilUpdates from "./ProfilComponents/FetchProfilUpdates.js";
import EditProfil from "./ProfilModal/EditProfil.js";
import QuickLinks from "./ProfilComponents/QuickLinks.js";
import CalendarSync from "./ProfilComponents/CalendarSync.js";
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
import { dateFilter } from '../../../tabulator/filters/Dates.js';
@@ -26,6 +27,7 @@ export default {
FetchProfilUpdates,
EditProfil,
QuickLinks,
CalendarSync,
},
inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"],
data() {
@@ -102,6 +104,7 @@ export default {
props: {
data: Object,
editData: Object,
calendarSyncUrls: Array,
},
provide() {
return {
@@ -426,13 +429,18 @@ export default {
</div>
</div>
<!-- START OF THE SECOND ROW IN THE SIDE PANEL -->
<div class="row">
<div class="row mb-3">
<div class="col">
<!-- HIER SIND DIE MAILVERTEILER -->
<mailverteiler :title="$p.t('profil','mailverteiler')" :data="data?.mailverteiler"></mailverteiler>
</div>
<!-- END OF THE SECOND ROW IN THE SIDE PANEL -->
</div>
<div class="row">
<div class="col">
<calendar-sync :uid="$props.data.username" :calendarSyncUrls="$props.calendarSyncUrls"></calendar-sync>
</div>
</div>
<!-- END OF SIDE PANEL -->
</div>
<!-- END OF CONTAINER ROW-->
+144 -1
View File
@@ -29562,7 +29562,8 @@ array(
'insertvon' => 'system'
)
)
), array(
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'sem_short',
@@ -29582,6 +29583,126 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Persönlichen LV-Plan abonnieren',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Subscribe to personal schedule',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_instructions',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anleitung LV-Plan Synchronisation',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Instructions for synchronizing your schedule',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_cal_dav',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'CalDAV URL (Android, Thunderbird, CalDAV-Synchronizer)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'CalDAV URL (Android, Thunderbird, CalDAV-Synchronizer)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_cal_dav_principal',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'CalDAV Principal URL (MacOS, iOS)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'CalDAV Principal URL (MacOS, iOS)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_i_cal',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'iCAL URL (Google)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'iCAL URL (Google)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'calendar_sync_clipboard_copy_confirmation',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'URL in die Zwischenablage kopiert.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'URL copied to clipboard.',
'description' => '',
'insertvon' => 'system'
)
)
),
//Profil Phrasen ende
// LvPlan Phrasen start
array(
@@ -57420,6 +57541,28 @@ I have been informed that I am under no obligation to consent to the transmissio
)
),
// ### LvPlanStgOrg END ###
// DMS-Link Phrasen Start
array(
'app' => 'core',
'category' => 'DMS-Link',
'phrase' => 'lvplanSyncFAQ',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '7188',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '7188',
'description' => '',
'insertvon' => 'system'
)
)
),
// DMS-Link Phrasen End
);