Breadcrumb: Get course title

Add CourseExtension.php to filter access of all courses.
pull/4017/head
Julio 4 years ago
parent bbaf61455f
commit 50c5fc8045
  1. 29
      assets/vue/components/Breadcrumb.vue
  2. 38
      assets/vue/store/modules/crud.js
  3. 33
      assets/vue/views/documents/List.vue
  4. 93
      src/CoreBundle/DataProvider/Extension/CourseExtension.php
  5. 7
      src/CoreBundle/Entity/Course.php

@ -37,6 +37,9 @@ export default {
...mapGetters('resourcenode', {
resourceNode: 'getResourceNode',
}),
...mapGetters('course', {
course: 'getCourse',
}),
items() {
console.log('Breadcrumb.vue');
console.log(this.$route.name);
@ -53,7 +56,6 @@ export default {
'MyCourses',
'MySessions',
'Home',
'CCalendarEventList',
];
if (list.includes(this.$route.name)) {
@ -70,6 +72,7 @@ export default {
}*/
if (this.legacy) {
console.log('legacy');
// Checking data from legacy main (1.11.x)
const mainUrl = window.location.href;
const mainPath = mainUrl.indexOf("main/");
@ -95,9 +98,6 @@ export default {
}
}
console.log(items);
if (this.resourceNode) {
console.log('resourceNode');
let folderParams = this.$route.query;
var queryParams = '';
for (var key in folderParams) {
@ -107,6 +107,17 @@ export default {
queryParams += key + '=' + encodeURIComponent(folderParams[key]);
}
// course is set in documents/List.vue
if (this.course) {
// First node
items.push({
text: this.course.title,
href: '/course/' + this.course.id + '/home?'+queryParams
});
}
if (this.resourceNode) {
console.log('resourceNode');
console.log(this.resourceNode);
console.log(this.resourceNode.path);
@ -114,16 +125,6 @@ export default {
const { path, matched } = this.$route;
const lastItem = matched[matched.length - 1];
// Get course
let courseId = this.$route.query.cid;
if (courseId) {
let courseCode = this.$route.query.cid;
items.push({
text: courseCode,
href: '/course/' + courseId + '/home?'+queryParams
});
}
for (let i = 0, len = parts.length; i < len; i += 1) {
let route = parts[i];
let routeParts = route.split('-');

@ -17,7 +17,8 @@ const initialState = () => ({
updated: null,
view: null,
violations: null,
resourceNode: null
resourceNode: null,
course: null,
});
const handleError = (commit, e) => {
@ -57,7 +58,8 @@ export const ACTIONS = {
SET_VIEW: 'SET_VIEW',
SET_VIOLATIONS: 'SET_VIOLATIONS',
TOGGLE_LOADING: 'TOGGLE_LOADING',
ADD_RESOURCE_NODE: 'ADD_RESOURCE_NODE'
ADD_RESOURCE_NODE: 'ADD_RESOURCE_NODE',
ADD_COURSE: 'ADD_COURSE'
};
export default function makeCrudModule({
@ -247,6 +249,29 @@ export default function makeCrudModule({
})
.catch(e => handleError(commit, e));
},
findCourse: ({ commit }, params) => {
const id = params['id'];
delete params['id'];
if (!service) throw new Error('No service specified!');
console.log('findCourse');
commit(ACTIONS.TOGGLE_LOADING);
return service
.find(id, params)
.then(response => {
if (200 === response.status) {
return response.json();
}
})
.then(item => {
commit(ACTIONS.TOGGLE_LOADING);
commit(ACTIONS.ADD_COURSE, item);
return item;
})
.catch(e => handleError(commit, e));
},
findResourceNode: ({ commit }, params) => {
const id = params['id'];
delete params['id'];
@ -324,9 +349,18 @@ export default function makeCrudModule({
getResourceNode: (state) => {
return state.resourceNode;
},
getCourse: (state) => {
return state.course;
},
},
mutations: {
updateField,
[ACTIONS.ADD_COURSE]: (state, item) => {
state.course = item;
state.isLoading = false;
//this.$set(state, 'resourceNode', item);
//this.$set(state, 'isLoading', false);
},
[ACTIONS.ADD_RESOURCE_NODE]: (state, item) => {
state.resourceNode = item;
state.isLoading = false;

@ -268,7 +268,7 @@
<Dialog v-model:visible="deleteMultipleDialog" :style="{width: '450px'}" header="Confirm" :modal="true">
<div class="confirmation-content">
<i class="pi pi-exclamation-triangle p-mr-3" style="font-size: 2rem" />
<span v-if="item">Are you sure you want to delete the selected items?</span>
<span v-if="item">{{ $t('Are you sure you want to delete the selected items?') }}</span>
</div>
<template #footer>
<Button label="No" icon="pi pi-times" class="p-button-text" @click="deleteMultipleDialog = false"/>
@ -320,7 +320,7 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import {mapActions, mapGetters, useStore} from 'vuex';
import { mapFields } from 'vuex-map-fields';
import ListMixin from '../../mixins/ListMixin';
import ActionCell from '../../components/ActionCell.vue';
@ -330,6 +330,9 @@ import ResourceFileLink from '../../components/documents/ResourceFileLink.vue';
import DataFilter from '../../components/DataFilter';
import DocumentsFilterForm from '../../components/documents/Filter';
import {RESOURCE_LINK_PUBLISHED, RESOURCE_LINK_DRAFT} from "../../components/resource_links/visibility";
import isEmpty from "lodash/isEmpty";
import toInteger from "lodash/toInteger";
import {useRoute, useRouter} from "vue-router";
export default {
name: 'DocumentsList',
@ -342,6 +345,20 @@ export default {
DataFilter
},
mixins: [ListMixin],
setup() {
const store = useStore();
const route = useRoute();
// Set resource node.
let id = route.params.node;
if (isEmpty(id)) {
id = route.query.node;
}
let cid = toInteger(route.query.cid);
id = '/api/courses/' + cid
const params = {id };
store.dispatch('course/findCourse', params);
},
data() {
return {
RESOURCE_LINK_PUBLISHED: RESOURCE_LINK_PUBLISHED,
@ -370,14 +387,10 @@ export default {
deleteItemDialog: false,
deleteMultipleDialog: false,
item: {},
filters: {},
filters: {'loadNode' : 1},
submitted: false,
};
},
created() {
//console.log('created - vue/views/documents/List.vue');
this.filters['loadNode'] = 1;
},
mounted() {
this.filters['loadNode'] = 1;
this.onUpdateOptions(this.options);
@ -434,11 +447,6 @@ export default {
methods: {
// prime
onPage(event) {
console.log('onPage');
console.log(event.page);
console.log(event.sortField);
console.log(event.sortOrder);
this.options.itemsPerPage = event.rows;
this.options.page = event.page + 1;
this.options.sortBy = event.sortField;
@ -471,7 +479,6 @@ export default {
if (this.item.title.trim()) {
if (this.item.id) {
} else {
//this.products.push(this.product);
this.item.filetype = 'folder';
this.item.parentResourceNodeId = this.$route.params.node;
this.item.resourceLinkList = JSON.stringify([{

@ -0,0 +1,93 @@
<?php
/* For licensing terms, see /license.txt */
declare(strict_types=1);
namespace Chamilo\CoreBundle\DataProvider\Extension;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
//use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\ResourceLink;
use Chamilo\CourseBundle\Entity\CDocument;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
/**
* Extension is called when loading api/courses.json.
*/
final class CourseExtension implements QueryCollectionExtensionInterface
{
private Security $security;
private RequestStack $requestStack;
public function __construct(Security $security, RequestStack $request)
{
$this->security = $security;
$this->requestStack = $request;
}
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null): void
{
$this->addWhere($queryBuilder, $resourceClass);
}
/*public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = []): void
{
error_log('applyToItem');
$this->addWhere($queryBuilder, $resourceClass);
}*/
private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
{
if (Course::class !== $resourceClass) {
return;
}
if ($this->security->isGranted('ROLE_ADMIN')) {
return;
}
if (null === $user = $this->security->getUser()) {
throw new AccessDeniedException('Access Denied.');
}
$request = $this->requestStack->getCurrentRequest();
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder
->innerJoin("$rootAlias.resourceNode", 'node')
->innerJoin('node.resourceLinks', 'links')
;
// Do not show deleted resources.
$queryBuilder
->andWhere('links.visibility != :visibilityDeleted')
->setParameter('visibilityDeleted', ResourceLink::VISIBILITY_DELETED)
;
$allowDraft =
$this->security->isGranted('ROLE_ADMIN') ||
$this->security->isGranted('ROLE_CURRENT_COURSE_TEACHER')
;
if (!$allowDraft) {
$queryBuilder
->andWhere('links.visibility != :visibilityDraft')
->setParameter('visibilityDraft', ResourceLink::VISIBILITY_DRAFT)
;
}
/*$queryBuilder->
andWhere('node.creator = :current_user')
;*/
//$queryBuilder->andWhere(sprintf('%s.node.creator = :current_user', $rootAlias));
//$queryBuilder->setParameter('current_user', $user->getId());
}
}

@ -40,7 +40,12 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource(
iri: 'https://schema.org/Course',
attributes: [
'security' => "is_granted('ROLE_ADMIN')",
'security' => "is_granted('ROLE_USER')",
],
itemOperations: [
'get' => [
'security' => "is_granted('VIEW', object)",
],
],
normalizationContext: [
'groups' => ['course:read'],

Loading…
Cancel
Save