diff --git a/application/controllers/Cis/Profil.php b/application/controllers/Cis/Profil.php index 77f6ffb07..83b8f5493 100644 --- a/application/controllers/Cis/Profil.php +++ b/application/controllers/Cis/Profil.php @@ -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), + ], + ]; + } } diff --git a/public/js/components/Cis/Profil/MitarbeiterProfil.js b/public/js/components/Cis/Profil/MitarbeiterProfil.js index 98b5a822b..44e0ca6eb 100644 --- a/public/js/components/Cis/Profil/MitarbeiterProfil.js +++ b/public/js/components/Cis/Profil/MitarbeiterProfil.js @@ -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 { -
+
+
+
+ +
+
diff --git a/public/js/components/Cis/Profil/Profil.js b/public/js/components/Cis/Profil/Profil.js index c2d64982c..b5f3fd552 100644 --- a/public/js/components/Cis/Profil/Profil.js +++ b/public/js/components/Cis/Profil/Profil.js @@ -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 = {
- +
`, -} +}; -export default Profil \ No newline at end of file +export default Profil; diff --git a/public/js/components/Cis/Profil/ProfilComponents/CalendarSync.js b/public/js/components/Cis/Profil/ProfilComponents/CalendarSync.js new file mode 100644 index 000000000..9f0df6dd5 --- /dev/null +++ b/public/js/components/Cis/Profil/ProfilComponents/CalendarSync.js @@ -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: ` +
+
+ {{ $p.t("profil/calendar_sync") }} +
+
+ +
+
`, +}; diff --git a/public/js/components/Cis/Profil/StudentProfil.js b/public/js/components/Cis/Profil/StudentProfil.js index 638bb15b4..e3e90d3bf 100644 --- a/public/js/components/Cis/Profil/StudentProfil.js +++ b/public/js/components/Cis/Profil/StudentProfil.js @@ -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 { -
+
+
+
+ +
+
diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index d7e6aab68..c1633bd9c 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -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 );