Skip to content
Snippets Groups Projects
Commit e9194413 authored by Marcus Eibrink-Lunzenauer's avatar Marcus Eibrink-Lunzenauer
Browse files

Marry course manager with the caching structure loading.

Refs #446.
parent e93d5c14
No related branches found
No related tags found
No related merge requests found
......@@ -8,6 +8,7 @@ use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\BadRequestException;
use JsonApi\Errors\RecordNotFoundException;
use JsonApi\Errors\UnprocessableEntityException;
use JsonApi\Providers\JsonApiConfig as C;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
......@@ -26,24 +27,36 @@ class StructuralElementsCopy extends NonJsonApiController
{
$data = $request->getParsedBody()['data'];
$remote_element = \Courseware\StructuralElement::find($data['element']['id']);
$parent_element = \Courseware\StructuralElement::find($data['parent_id']);
if (!Authority::canCreateContainer($user = $this->getUser($request), $parent_element)) {
$sourceElement = StructuralElement::find($args['id']);
$newParent = StructuralElement::find($data['parent_id']);
if (!Authority::canCreateContainer($user = $this->getUser($request), $newParent)) {
throw new AuthorizationFailedException();
}
$new_element = $this->copyElement($user, $remote_element, $parent_element);
$newElement = $this->copyElement($user, $sourceElement, $newParent);
$response = $response->withHeader('Content-Type', 'application/json');
$response->getBody()->write((string) json_encode($new_element));
return $response;
return $this->redirectToStructuralElement($response, $newElement);
}
private function copyElement(\User $user, \Courseware\StructuralElement $remote_element, \Courseware\StructuralElement $parent_element)
private function copyElement(\User $user, StructuralElement $sourceElement, StructuralElement $newParent)
{
$new_element = $remote_element->copy($user, $parent_element);
$new_element = $sourceElement->copy($user, $newParent);
return $new_element;
}
/**
* @SuppressWarnings(PHPMD.Superglobals)
*/
private function redirectToStructuralElement(Response $response, StructuralElement $resource): Response
{
$pathinfo = $this->getSchema($resource)
->getSelfSubLink($resource)
->getSubHref();
$old = \URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']);
$url = \URLHelper::getURL($this->container->get(C::JSON_URL_PREFIX) . $pathinfo, [], true);
\URLHelper::setBaseURL($old);
return $response->withRedirect($url, 303);
}
}
......@@ -88,7 +88,7 @@
</courseware-tab>
<courseware-tab :name="$gettext('Kopieren')">
<courseware-manager-copy-selector @loadSelf="reloadElements"/>
<courseware-manager-copy-selector @loadSelf="reloadElements" @reloadElement="reloadElements" />
</courseware-tab>
<courseware-tab :name="$gettext('Importieren')">
......@@ -233,6 +233,7 @@ export default {
async reloadElements() {
await this.setCurrentId(this.currentId);
await this.setSelfId(this.selfId);
this.$emit("reload");
},
async setCurrentId(target) {
this.currentId = target;
......
......@@ -32,6 +32,7 @@
:currentElement="remoteElement"
@selectElement="setRemoteId"
@loadSelf="loadSelf"
@reloadElement="reloadElement"
/>
<courseware-companion-box
v-if="remoteId === '' && hasRemoteCid"
......@@ -106,9 +107,9 @@ export default {
},
methods: {
...mapActions({
loadAnotherCourseware: 'courseware-structure/loadAnotherCourseware',
loadUsersCourses: 'loadUsersCourses',
loadStructuralElement: 'loadStructuralElement',
loadRemoteCoursewareStructure: 'loadRemoteCoursewareStructure',
loadSemester: 'semesters/loadById',
}),
selectSource(source) {
......@@ -116,16 +117,15 @@ export default {
},
async loadRemoteCourseware(cid) {
this.remoteCid = cid;
this.remoteCoursewareInstance = await this.loadRemoteCoursewareStructure({rangeId: this.remoteCid, rangeType: 'courses'});
this.remoteCoursewareInstance = await this.loadAnotherCourseware({ id: this.remoteCid, type: 'courses'});
if (this.remoteCoursewareInstance !== null) {
this.setRemoteId(this.remoteCoursewareInstance.relationships.root.data.id);
} else {
this.remoteId = '';
}
},
async loadOwnCourseware() {
this.ownCoursewareInstance = await this.loadRemoteCoursewareStructure({rangeId: this.userId, rangeType: 'users'});
this.ownCoursewareInstance = await this.loadAnotherCourseware({ id: this.userId, type: 'users' });
if (this.ownCoursewareInstance !== null) {
this.setOwnId(this.ownCoursewareInstance.relationships.root.data.id);
} else {
......@@ -188,6 +188,9 @@ export default {
}
return 'seminar';
},
reloadElement() {
this.$emit("reloadElement");
}
},
async mounted() {
......
......@@ -309,7 +309,7 @@ export default {
let element = data.element;
if (source === 'self') {
element.relationships.parent.data.id = this.filingData.parentItem.id;
element.attributes.position = this.childrenById(this.filingData.parentItem.id).length;
element.attributes.position = this.childrenById(this.filingData.parentItem.id).length + 1;
await this.lockObject({ id: element.id, type: 'courseware-structural-elements' });
await this.updateStructuralElement({
element: element,
......@@ -343,7 +343,7 @@ export default {
let container = data.container;
if (source === 'self') {
container.relationships['structural-element'].data.id = this.filingData.parentItem.id;
container.attributes.position = this.filingData.parentItem.relationships.containers.data.length;
container.attributes.position = this.filingData.parentItem.relationships.containers.data.length + 1;
await this.lockObject({id: container.id, type: 'courseware-containers'});
await this.updateContainer({
container: container,
......@@ -399,7 +399,7 @@ export default {
await this.unlockObject({id: destinationContainer.id, type: 'courseware-containers'});
block.relationships.container.data.id = this.filingData.parentItem.id;
block.attributes.position = this.filingData.parentItem.relationships.blocks.data.length;
block.attributes.position = this.filingData.parentItem.relationships.blocks.data.length + 1;
await this.lockObject({id: block.id, type: 'courseware-blocks'});
await this.updateBlock({
block: block,
......
<template>
<courseware-course-manager></courseware-course-manager>
<courseware-course-manager @reload="rebuildStructure"></courseware-course-manager>
</template>
<script>
......@@ -20,17 +20,21 @@ export default {
invalidateStructureCache: 'courseware-structure/invalidateCache',
loadCoursewareStructure: 'courseware-structure/load',
}),
async rebuildStructure() {
// compute order of structural elements once more
await this.buildStructure();
console.debug("built structure")
// throw away stale cache
this.invalidateStructureCache();
},
},
async mounted() {
await this.loadCoursewareStructure();
},
watch: {
async structuralElements(newElements, oldElements) {
// compute order of structural elements once more
await this.buildStructure();
// throw away stale cache
this.invalidateStructureCache();
this.rebuildStructure();
},
},
};
......
......@@ -317,16 +317,13 @@ export const actions = {
// console.log(resp);
});
},
copyStructuralElement({ getters }, { parentId, element }) {
const copy = {
data: {
element: element,
parent_id: parentId,
},
};
copyStructuralElement({ dispatch, getters }, { parentId, element }) {
const copy = { data: { parent_id: parentId, }, };
return state.httpClient.post(`courseware-structural-elements/${element.id}/copy`, copy).then((resp) => {
// console.log(resp);
return state.httpClient.post(`courseware-structural-elements/${element.id}/copy`, copy)
.then(({ data }) => {
const id = data.data.id;
dispatch('loadStructuralElement', id);
});
},
......
......@@ -31,14 +31,21 @@ export const mutations = {
const actions = {
build({ commit, rootGetters }) {
const structuralElements = rootGetters['courseware-structural-elements/all'];
const root = findRoot(structuralElements);
const instance = rootGetters['courseware'];
if (!instance) {
throw new Error('Could not find current courseware');
}
const root = rootGetters['courseware-structural-elements/related']({
parent: { id: instance.id, type: instance.type },
relationship: 'root',
});
if (!root) {
commit('reset');
return;
}
const structuralElements = rootGetters['courseware-structural-elements/all'];
const children = structuralElements.reduce((memo, element) => {
const parent = element.relationships.parent?.data?.id ?? null;
if (parent) {
......@@ -81,43 +88,70 @@ const actions = {
}
},
// load the structure of the current courseware
async load({ commit, dispatch, rootGetters }) {
const parent = rootGetters['context'];
const relationship = 'courseware';
const options = {
include: 'bookmarks,root',
};
const context = rootGetters['context'];
const instance = await dispatch('loadInstance', context);
commit('coursewareSet', instance, { root: true });
const root = rootGetters['courseware-structural-elements/related']({
parent: { id: instance.id, type: instance.type },
relationship: 'root',
});
if (!root) {
throw new Error(`Could not find root of courseware { id: ${instance.id}, type: ${instance.type}`);
}
// get courseware instance
await dispatch(`courseware-instances/loadRelated`, { parent, relationship, options }, { root: true });
const courseware = rootGetters['courseware-instances/all'][0];
commit('coursewareSet', courseware, { root: true });
dispatch('fetchDescendantsWithCaching', { root });
// load descendants
dispatch('fetchDescendants');
return instance;
},
async fetchDescendants({ dispatch, rootGetters, commit }) {
// get root of that instance
const courseware = rootGetters['courseware'];
if (!courseware) {
return;
}
const rootElement = rootGetters['courseware-structural-elements/related']({
parent: { id: courseware.id, type: courseware.type },
// load the structure of a specified courseware
async loadAnotherCourseware({ commit, dispatch, rootGetters }, context) {
const instance = await dispatch('loadInstance', context);
const root = rootGetters['courseware-structural-elements/related']({
parent: { id: instance.id, type: instance.type },
relationship: 'root',
});
if (!rootElement) {
return;
if (!root) {
throw new Error(`Could not find root of courseware { id: ${instance.id}, type: ${instance.type}`);
}
await dispatch('loadDescendants', { root });
return instance;
},
loadInstance({ commit, dispatch, rootGetters }, context) {
const parent = context;
const relationship = 'courseware';
const options = {
include: 'bookmarks,root',
};
return dispatch(
`courseware-instances/loadRelated`,
{
parent,
relationship,
options,
},
{ root: true }
).then(() => {
return rootGetters['courseware-instances/related']({ parent, relationship });
});
},
async fetchDescendantsWithCaching({ dispatch, rootGetters, commit }, { root }) {
const cache = window.STUDIP.Cache.getInstance('courseware');
const cacheKey = `descendants/${rootElement.id}/${rootGetters['userId']}`;
const cacheKey = `descendants/${root.id}/${rootGetters['userId']}`;
await unpickleDescendants();
revalidateDescendants();
await unpickleStaleDescendants();
return revalidateDescendants();
function unpickleDescendants() {
function unpickleStaleDescendants() {
try {
const descendants = cache.get(cacheKey);
const cacheHit = descendants !== undefined;
......@@ -130,22 +164,7 @@ const actions = {
}
function revalidateDescendants() {
return loadDescendants().then(removeStaleElements).then(pickleDescendants);
}
function loadDescendants() {
const parent = { id: rootElement.id, type: rootElement.type };
const relationship = 'descendants';
const options = {
'page[offset]': 0,
'page[limit]': 10000,
};
return dispatch(
'courseware-structural-elements/loadRelated',
{ parent, relationship, options },
{ root: true }
);
return dispatch('loadDescendants', { root }).then(removeStaleElements).then(pickleDescendants);
}
function pickleDescendants() {
......@@ -156,9 +175,9 @@ const actions = {
function removeStaleElements() {
const idsToKeep = [
rootElement.id,
root.id,
...rootGetters['courseware-structural-elements/related']({
parent: rootElement,
parent: root,
relationship: 'descendants',
}).map(({ id }) => id),
];
......@@ -168,11 +187,22 @@ const actions = {
.forEach((id) => commit('courseware-structural-elements/REMOVE_RECORD', { id }, { root: true }));
}
},
loadDescendants({ dispatch }, { root }) {
const parent = { id: root.id, type: root.type };
const relationship = 'descendants';
const options = {
'page[offset]': 0,
'page[limit]': 10000,
};
function findRoot(nodes) {
return nodes.find((node) => !node.relationships.parent?.data);
}
return dispatch(
'courseware-structural-elements/loadRelated',
{ parent, relationship, options },
{ root: true }
);
},
};
function* visitTree(tree, current) {
if (current) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment