Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
>
{{ category(node.categories) }}
</span>
<span v-if="(isTopic && node.coach_count) || isCoach">
<span v-if="isTopic && node.coach_count">
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a bug-- the coach icon was showing twice but this one should only show when its a topic

<!-- for each learning activity -->
<VTooltip bottom lazy>
<template #activator="{ on }">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import flatMap from 'lodash/flatMap';
import uniq from 'lodash/uniq';
import { NEW_OBJECT, NOVALUE } from 'shared/constants';
import client from 'shared/client';
import { RELATIVE_TREE_POSITIONS, CHANGES_TABLE, TABLE_NAMES } from 'shared/data/constants';
import {
RELATIVE_TREE_POSITIONS,
CHANGES_TABLE,
TABLE_NAMES,
IGNORED_SOURCE,
} from 'shared/data/constants';
import { ContentNode } from 'shared/data/resources';
import { ContentKindsNames } from 'shared/leUtils/ContentKinds';
import { findLicense } from 'shared/utils/helpers';
Expand Down Expand Up @@ -152,8 +157,9 @@ export function createContentNode(context, { parent, kind, ...payload }) {
const channel = context.rootGetters['currentChannel/currentChannel'];
let contentDefaults = Object.assign({}, channel.content_defaults);

if (kind === ContentKindsNames.TOPIC) {
// Topics shouldn't have license, language or copyright info assigned.
const isFolder = kind === ContentKindsNames.TOPIC;
if (isFolder) {
// Folders shouldn't have license, language or copyright info assigned.
contentDefaults = {};
} else {
// content_defaults for historical reason has stored the license as a string constant,
Expand Down Expand Up @@ -191,11 +197,20 @@ export function createContentNode(context, { parent, kind, ...payload }) {
assessmentItems: [],
files: [],
});
return ContentNode.put(contentNodeData).then(id => {
return ContentNode.put(contentNodeData).then(async id => {
context.commit('ADD_CONTENTNODE', {
id,
...contentNodeData,
});
// Update ancestor's counts upon adding this new folder or resource
await ContentNode.updateAncestors({ id, source: IGNORED_SOURCE }, ancestor => {
return {
total_count: ancestor.total_count + 1,
resource_count: ancestor.resource_count + Number(!isFolder),
coach_count:
ancestor.coach_count + Number(contentNodeData.role_visibility === RolesNames.COACH),
};
});
return id;
});
}
Expand Down Expand Up @@ -355,7 +370,20 @@ export function updateContentNode(context, { id, ...payload } = {}) {
};

context.commit('ADD_CONTENTNODE', { id, ...contentNodeData });
return ContentNode.update(id, contentNodeData);
return ContentNode.update(id, contentNodeData).then(async updated => {
// If the visibility changed, update ancestor coach counts appropriately
if (node.role_visibility !== contentNodeData.role_visibility) {
const coachCountDiff = contentNodeData.role_visibility === RolesNames.COACH ? 1 : -1;

await ContentNode.updateAncestors({ id, source: IGNORED_SOURCE }, ancestor => {
return {
coach_count: ancestor.coach_count + coachCountDiff,
};
});
}

return updated;
});
}

export function addTags(context, { ids, tags }) {
Expand Down Expand Up @@ -404,8 +432,25 @@ export function copyContentNode(
) {
// First, this will parse the tree and create the copy the local tree nodes,
// with a `source_id` of the source node then create the content node copies
return ContentNode.copy(id, target, position, excluded_descendants).then(node => {
return ContentNode.copy(id, target, position, excluded_descendants).then(async node => {
context.commit('ADD_CONTENTNODE', node);

// Update ancestors' counts after copying
const resourceCountDiff =
node['kind'] === ContentKindsNames.TOPIC ? node['resource_count'] || 0 : 1;
const coachCountDiff =
node['kind'] === ContentKindsNames.TOPIC
? node['coach_count'] || 0
: Number(node['role_visibility'] === RolesNames.COACH);

await ContentNode.updateAncestors({ id, source: IGNORED_SOURCE }, ancestor => {
return {
total_count: ancestor.total_count + (node['total_count'] || 1),
resource_count: ancestor.resource_count + resourceCountDiff,
coach_count: ancestor.coach_count + coachCountDiff,
};
});

return node;
});
}
Expand Down Expand Up @@ -434,11 +479,37 @@ export function moveContentNodes(
}

return Promise.all(
id__in.map(id => {
return ContentNode.move(id, target, position).then(node => {
context.commit('ADD_CONTENTNODE', node);
return id;
id__in.map(async id => {
const node = context.getters.getContentNode(id) || {};

const resourceCountDiff =
node['kind'] === ContentKindsNames.TOPIC ? node['resource_count'] || 0 : 1;
const coachCountDiff =
node['kind'] === ContentKindsNames.TOPIC
? node['coach_count'] || 0
: Number(node['role_visibility'] === RolesNames.COACH);

// Decrement ancestors' counts prior to the move
await ContentNode.updateAncestors({ id, source: IGNORED_SOURCE }, ancestor => {
return {
total_count: ancestor.total_count - (node['total_count'] || 1),
resource_count: ancestor.resource_count - resourceCountDiff,
coach_count: ancestor.coach_count - coachCountDiff,
};
});

// Move the node and update Vuex state
context.commit('ADD_CONTENTNODE', await ContentNode.move(id, target, position));

// Increment ancestors' counts after the move
await ContentNode.updateAncestors({ id, source: IGNORED_SOURCE }, ancestor => {
return {
total_count: ancestor.total_count + (node['total_count'] || 1),
resource_count: ancestor.resource_count + resourceCountDiff,
coach_count: ancestor.coach_count + coachCountDiff,
};
});
return id;
})
);
}
Expand Down
26 changes: 26 additions & 0 deletions contentcuration/contentcuration/frontend/shared/data/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,11 @@ export const ContentNode = new TreeResource({
return payload;
},

/**
* Returns all ancestors of a node, including itself
* @param {String} id
* @return {Promise<Object[]>}
*/
getAncestors(id) {
return this.table.get(id).then(node => {
if (node) {
Expand All @@ -1451,6 +1456,27 @@ export const ContentNode = new TreeResource({
});
},

/**
* Calls `updateCallback` on each ancestor, and calls `.update` for that ancestor
* with the return value from `updateCallback`
*
* @param {String} id
* @param {String|null} source
* @param {Function} updateCallback
* @return {Promise<void>}
*/
updateAncestors({ id, source = null }, updateCallback) {
return this.transaction({ mode: 'rw', source }, async () => {
const ancestors = await this.getAncestors(id);
for (let ancestor of ancestors) {
if (ancestor.id === id) {
continue;
}
await this.update(ancestor.id, updateCallback(ancestor));
}
});
},

/**
* Uses local IndexedDB index on node_id+channel_id, otherwise specifically requests the
* collection using the same params since GET detail endpoint doesn't support that the params
Expand Down