Compare commits

..
4 changed files with 161 additions and 234 deletions
@@ -198,7 +198,7 @@ class Grades extends FHCAPI_Controller
$now = date('c');
// NOTE(chris): Stg Permissions
if (!$this->hasPermissionUpdate($lehrveranstaltung_id, $student_uid))
if (!$this->hasPermissionGradeUID($lehrveranstaltung_id, $student_uid))
return $this->_outputAuthError([$this->router->method => ['admin', 'assistenz']]);
$this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel');
@@ -260,7 +260,7 @@ class Grades extends FHCAPI_Controller
$lehrveranstaltung_id = $this->input->post('lehrveranstaltung_id');
// NOTE(chris): Stg Permissions
if (!$this->hasPermissionDelete($lehrveranstaltung_id, $student_uid))
if (!$this->hasPermissionGradeUID($lehrveranstaltung_id, $student_uid))
return $this->_outputAuthError([$this->router->method => ['admin', 'assistenz']]);
$this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel');
@@ -298,7 +298,7 @@ class Grades extends FHCAPI_Controller
$authUID = getAuthUID();
// NOTE(chris): Stg Permissions
if (!$this->hasPermissionCopy($lehrveranstaltung_id, $student_uid))
if (!$this->hasPermissionGradeUID($lehrveranstaltung_id, $student_uid))
return $this->_outputAuthError([$this->router->method => 'student/noten']);
$this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel');
@@ -410,7 +410,7 @@ class Grades extends FHCAPI_Controller
$repeaterGrade = current($repeaterGrade);
// NOTE(chris): Stg Permissions
if (!$this->hasPermissionCopy($repeaterGrade->lehrveranstaltung_id, $repeaterGrade->student_uid))
if (!$this->hasPermissionGradeUID($repeaterGrade->lehrveranstaltung_id, $repeaterGrade->student_uid))
return $this->_outputAuthError([$this->router->method => 'student/noten']);
$data = [
@@ -565,136 +565,57 @@ class Grades extends FHCAPI_Controller
}
/**
* Helper function to check permissions for updateCertificate()
* Combined helper function to check permissions for
* certificate-modifying functions
*
* @param integer $lehrveranstaltung_id
* @param string $student_uid
*
* @return boolean
*/
protected function hasPermissionUpdate($lehrveranstaltung_id, $student_uid)
protected function hasPermissionGradeUID($lehrveranstaltung_id, $student_uid)
{
if ($lehrveranstaltung_id === null || $student_uid === null)
return true;
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->load([$student_uid]);
if (isError($result) || !hasData($result))
$studentResult = $this->StudentModel->load([$student_uid]);
if (isError($studentResult) || !hasData($studentResult))
return false;
$student = current(getData($result));
$student = current(getData($studentResult));
if ($this->permissionlib->isBerechtigt('admin', 'suid', $student->studiengang_kz))
return true;
if ($this->permissionlib->isBerechtigt('assistenz', 'suid', $student->studiengang_kz))
return true;
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$result = $this->StudienplanModel->getAllOesForLv($lehrveranstaltung_id);
if (isError($result))
$oesResult = $this->StudienplanModel->getAllOesForLv($lehrveranstaltung_id);
if (isError($oesResult))
return false;
$oes = getData($result) ?: [];
$oes = hasData($oesResult) ? getData($oesResult) : [];
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$result = $this->LehrveranstaltungModel->getStg($lehrveranstaltung_id);
if (isError($result))
$lvResult = $this->LehrveranstaltungModel->load($lehrveranstaltung_id);
if (isError($lvResult))
return false;
if (hasData($result))
$oes[] = current(getData($result));
if (hasData($lvResult))
$oes[] = current(getData($lvResult));
if ($this->permissionlib->isBerechtigt('student/noten', 'suid', $student->studiengang_kz))
return true;
foreach ($oes as $oe) {
if ($this->permissionlib->isBerechtigt('admin', 'suid', $oe->oe_kurzbz))
if ($this->permissionlib->isBerechtigt('student/noten', 'suid', $oe->oe_kurzbz))
return true;
if ($this->permissionlib->isBerechtigt('assistenz', 'suid', $oe->oe_kurzbz))
if ($this->permissionlib->isBerechtigt('student/noten', 'suid', $oe->studiengang_kz))
return true;
}
return false;
}
/**
* Helper function to check permissions for deleteCertificate()
*
* @param integer $lehrveranstaltung_id
* @param string $student_uid
*
* @return boolean
*/
protected function hasPermissionDelete($lehrveranstaltung_id, $student_uid)
{
if ($lehrveranstaltung_id === null || $student_uid === null)
return true;
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->load([$student_uid]);
if (isError($result) || !hasData($result))
return false;
$student = current(getData($result));
if ($this->permissionlib->isBerechtigt('admin', 'suid', $student->studiengang_kz))
return true;
if ($this->permissionlib->isBerechtigt('assistenz', 'suid', $student->studiengang_kz))
return true;
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$result = $this->LehrveranstaltungModel->load($lehrveranstaltung_id);
if (isError($result) || !hasData($result))
return false;
$oe = current(getData($result));
if ($this->permissionlib->isBerechtigt('admin', 'suid', $oe->oe_kurzbz))
return true;
if ($this->permissionlib->isBerechtigt('assistenz', 'suid', $oe->oe_kurzbz))
return true;
return false;
}
/**
* Helper function to check permissions for
* copyTeacherProposalToCertificate() and copyRepeaterGradeToCertificate()
*
* @param integer $lehrveranstaltung_id
* @param string $student_uid
*
* @return boolean
*/
protected function hasPermissionCopy($lehrveranstaltung_id, $student_uid)
{
if ($lehrveranstaltung_id === null || $student_uid === null)
return true;
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->load([$student_uid]);
if (isError($result) || !hasData($result))
return false;
$student = current(getData($result));
if ($this->permissionlib->isBerechtigt('student/noten', 'suid', $student->studiengang_kz))
return true;
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$result = $this->LehrveranstaltungModel->load($lehrveranstaltung_id);
if (isError($result) || !hasData($result))
return false;
$oe = current(getData($result));
if ($this->permissionlib->isBerechtigt('student/noten', 'suid', $oe->oe_kurzbz))
return true;
return false;
}
}
+15 -13
View File
@@ -28,7 +28,6 @@ export default {
urlMatchRankings:[],
navUserDropdown:null,
menuOpen:true,
openMenuHierarchy: [],
};
},
inject: ["isNarrow", "isMobile"],
@@ -36,7 +35,7 @@ export default {
return{
setActiveEntry: this.setActiveEntry,
addUrlCount: this.addUrlCount,
setOpenMenuHierarchy: this.setOpenMenuHierarchy,
makeParentContentActive: this.makeParentContentActive,
}
},
computed:{
@@ -83,6 +82,19 @@ export default {
handleHideNavUser(){
document.removeEventListener("click", this.checkSettingsVisibility);
},
makeParentContentActive(content_id, collection=this.entries, parent=null){
if(!collection) return;
if (typeof collection == 'object' && !Array.isArray(collection) && Object.entries(collection).length > 0) {
collection = Object.values(collection);
}
for(let entry of collection){
if(entry.content_id == content_id){
this.activeEntry = parent;
}
this.makeParentContentActive(content_id, entry.childs, entry.content_id);
}
},
addUrlCount(count){
this.urlMatchRankings.push(count);
},
@@ -90,9 +102,6 @@ export default {
setActiveEntry(content_id){
this.activeEntry = content_id;
},
setOpenMenuHierarchy(openMenuHierarchy) {
this.openMenuHierarchy = openMenuHierarchy;
},
},
created(){
this.fetchMenu();
@@ -183,14 +192,7 @@ export default {
<div class="offcanvas-body p-0">
<div id="nav-main-menu" class="nav-menu-collapse collapse collapse-horizontal show">
<div class="flex-grow-1">
<cis-menu-entry
v-for="entry in entries"
:key="entry.content_id"
:highestMatchingUrlCount="highestMatchingUrlCount"
:activeContent="activeEntry"
:entry="entry"
:openMenuHierarchy="openMenuHierarchy"
/>
<cis-menu-entry :highestMatchingUrlCount="highestMatchingUrlCount" :activeContent="activeEntry" v-for="entry in entries" :key="entry.content_id" :entry="entry" />
</div>
</div>
</div>
+94 -90
View File
@@ -8,11 +8,6 @@ export default {
},
activeContent: [String, Number],
highestMatchingUrlCount: Number,
openMenuHierarchy: Array,
precedingMenuHierarchy: {
type: Array,
default: [],
},
},
data: () => {
return {
@@ -20,7 +15,7 @@ export default {
urlCount:0,
}
},
inject: ['setActiveEntry', 'setOpenMenuHierarchy', 'addUrlCount'],
inject: ['makeParentContentActive', 'setActiveEntry','addUrlCount'],
watch:{
highestMatchingUrlCount: function(newValue)
{
@@ -30,27 +25,49 @@ export default {
this.setActiveEntry(this.entry.content_id);
}
},
openMenuHierarchy() {
if (!this.hasChilds) return;
if (this.$props.openMenuHierarchy.length && this.$props.openMenuHierarchy[0] === this.$props.entry.content_id) {
this.$props.entry.menu_open = true;
} else {
this.$props.entry.menu_open = false;
activeContent: function(newValue){
if(newValue == this.entry.content_id){
// wenn der Menupunkt nicht bereits offen ist
if (!this.entry.menu_open){
this.entry.menu_open = true;
}
}else{
if (this.searchRecursiveChild(this.entry, 'content_id',newValue)) {
this.entry.menu_open = true;
} else {
this.entry.menu_open = false;
}
}
},
'entry.menu_open': function (isMenuOpen) {
if (!this.collapse)
return;
if (isMenuOpen) {
this.collapse.show();
} else {
this.collapse.hide();
'entry.menu_open': function (newValue,oldValue) {
if (newValue)
{
// only invokes .show if this.collapse is not null
this.collapse && this.collapse.show();
}
else
{
// only invokes .hide if this.collapse is not null
this.collapse && this.collapse.hide();
if (this.activeContent == this.entry.content_id)
{
this.makeParentContentActive(this.entry.content_id);
}
}
},
},
computed: {
active: function () {
if (this.entry.menu_open){
return true;
}
else if (this.activeContent) {
return this.activeContent == this.entry.content_id;
} else {
return false;
}
},
hasFullLink() {
return this.entry.url.startsWith(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router)
},
@@ -72,34 +89,25 @@ export default {
},
hasChilds() {
return this.entry.childs && this.entry.childs.length !== 0;
},
menuHierarchy() {
return [...this.$props.precedingMenuHierarchy, this.$props.entry.content_id];
},
menuNodeHref() {
if (this.hasChilds) {
return this.$props.entry.menu_open && this.hasFullLink
? this.$props.entry.url
: null;
} else {
return this.$props.entry.url;
}
},
}
},
methods: {
getUrlMatchPoints(url,link){
let splitted_link = link.split('/');
let splitted_url = url.href.split('/');
let count = 0;
for (let part_url of splitted_url) {
for (let part_link of splitted_link) {
if (part_url === part_link) {
for(let part_url of splitted_url)
{
for (let part_link of splitted_link)
{
if(part_url == part_link)
{
count++;
}
}
}
this.urlCount = count;
this.addUrlCount(count);
},
@@ -111,11 +119,14 @@ export default {
let url_hash = url.hash;
url_hash = url_hash.replace(url_hash_spaceSymbol_regex, " ").replace(url_hash_sharpSymbol_regex,"");
// if the url hash contains the titel of the menu
// or if the url equals the link of a menu
// then set the menu active
if (url_hash == this.entry.titel || url.href == this.entry.url) {
this.setActiveEntry(this.entry.content_id);
this.setOpenMenuHierarchy(this.menuHierarchy);
}
},
// searches the childs of an entry recursively based on the value of a property
searchRecursiveChild(entry,property,value){
if (typeof entry.childs == 'object' && !Array.isArray(entry.childs) && Object.entries(entry.childs).length > 0){
entry.childs = Object.values(entry.childs);
@@ -132,23 +143,16 @@ export default {
}
return false;
},
toggleCollapse() {
if (!this.$props.entry.menu_open) {
this.setOpenMenuHierarchy(this.menuHierarchy);
} else {
this.setOpenMenuHierarchy(this.precedingMenuHierarchy);
toggleCollapse(evt) {
if (this.active)
{
this.makeParentContentActive(this.entry.content_id);
}
},
handleClickOnMenuNode() {
if (this.hasFullLink) {
this.setActiveEntry(this.$props.entry.content_id);
if (this.hasChilds) {
this.setOpenMenuHierarchy(this.menuHierarchy);
}
} else if (this.hasChilds) {
this.toggleCollapse();
else
{
this.setActiveEntry(this.entry.content_id);
}
},
}
},
mounted() {
if (this.$refs.children) {
@@ -164,41 +168,41 @@ export default {
INCLUDE
</div>
<template v-else>
<div class="btn-group w-100">
<a
@click="handleClickOnMenuNode($event)"
:href="menuNodeHref"
:target="target"
class="btn btn-default rounded-0 text-start"
:class="{
['btn-level-' + level]: true,
'fw-bold': $props.activeContent === $props.entry.content_id
}"
:style="'padding-left: calc(var(--bs-btn-padding-x) * ' + $props.level + ');'"
>
{{ entry.titel }}
</a>
<button
v-if="hasChilds"
@click.prevent="toggleCollapse()"
:aria-expanded="entry.menu_open"
class="btn btn-default rounded-0 dropdown-toggle dropdown-toggle-split flex-grow-0"
:class="{ collapsed: !entry.menu_open }"
>
<span class="visually-hidden">Toggle Dropdown</span>
</button>
</div>
<ul v-if="hasChilds" ref="children" class="nav w-100 collapse" >
<cis-menu-entry
v-for="child in entry.childs"
:key="child"
:highestMatchingUrlCount="highestMatchingUrlCount"
:activeContent="activeContent"
:entry="child"
:level="level + 1"
:openMenuHierarchy="$props.openMenuHierarchy.slice(1)"
:precedingMenuHierarchy="menuHierarchy"
/>
</ul>
<template v-if="hasChilds">
<div class="btn-group w-100">
<a :target="target"
:href="(entry.menu_open && hasFullLink) ? entry.url : null"
@click="toggleCollapse"
:class="{
'btn btn-default rounded-0 text-start': true,
['btn-level-' + level]: true,
'fw-bold':active
}">
{{ entry.titel }}
</a>
<button @click.prevent="toggleCollapse" :aria-expanded="entry.menu_open"
:class="{
'btn btn-default rounded-0 dropdown-toggle dropdown-toggle-split flex-grow-0': true,
collapsed: !entry.menu_open
}">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
</div>
<ul ref="children"
class="nav w-100 collapse">
<cis-menu-entry :highestMatchingUrlCount="highestMatchingUrlCount" :activeContent="activeContent" v-for="child in entry.childs" :key="child" :entry="child" :level="level + 1"/>
</ul>
</template>
<a v-else
:href="entry.url"
:target="target"
:class="{
'btn btn-default rounded-0 w-100 text-start': true,
['btn-level-' + level]: true,
'fw-bold':active
}"
@mouseup="setActiveEntry(entry.content_id)">
{{ entry.titel }}
</a>
</template>`
};
@@ -822,33 +822,18 @@ export default {
@headerFilterOn="handleHeaderFilter"
>
<template #actions>
<!-- <div>
<button
class="btn btn-outline-success sm mb-1"
:title="'Export ' + selectedPrestudents.length + ' prestudent(s) to Excel'"
>
<i class="fas fa-file-excel fa-xl"></i>
</button>
</div> -->
<template v-if="filter.length || headerFilterActive">
<div class="d-flex justify-content-center align-items-center gap-2 ps-4 position-absolute start-50 translate-middle-x">
<p class="text-danger mb-0">
<strong>{{$p.t('filter','filterActive')}}</strong>
</p>
<button
class="btn btn-outline-danger sm"
:title="$p.t('filter/filterDelete')"
@click="resetFilter"
>
<span class="fa-solid fa-filter-circle-xmark"></span>
</button>
</div>
</template>
</template>
<!--
<template #actions>
<div>
<button
class="btn btn-outline-success sm mb-1"
:title="'Export ' + selectedPrestudents.length + ' prestudent(s) to Excel'"
>
<i class="fas fa-file-excel fa-xl"></i>
</button>
</div>
</template>
-->
<template #additional>
<div class="pe-1">
@@ -888,7 +873,22 @@ export default {
<i class="fa-solid fa-refresh pe-1"></i> {{selected.length}}
</button>
</div>
</template>
<template v-if="filter.length || headerFilterActive">
<div class="d-flex justify-content-center align-items-center gap-2 ps-4 position-absolute start-50 translate-middle-x">
<p class="text-danger mb-0">
<strong>{{$p.t('filter','filterActive')}}</strong>
</p>
<button
class="btn btn-outline-danger sm"
:title="$p.t('filter/filterDelete')"
@click="resetFilter"
>
<span class="fa-solid fa-filter-circle-xmark"></span>
</button>
</div>
</template>
<template #filter>