Skip to content
Merged
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 @@ -149,7 +149,7 @@
if (this.isTopic) {
return `${baseUrl}#/${this.node.id}`;
}
return `${baseUrl}#/${this.node.parent_id}/${this.node.id}`;
return `${baseUrl}#/${this.node.parent}/${this.node.id}`;
},
resourcesMsg() {
let count;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@

<script>

import { mapActions, mapMutations, mapState } from 'vuex';
import { mapActions, mapMutations, mapState, mapGetters } from 'vuex';
import sumBy from 'lodash/sumBy';
import { RouteNames } from '../../constants';
import ResourceDrawer from '../../components/ResourceDrawer';
Expand Down Expand Up @@ -114,6 +114,7 @@
},
computed: {
...mapState('importFromChannels', ['selected']),
...mapGetters('contentNode', ['getContentNode']),
dialog: {
get() {
return IMPORT_ROUTES.includes(this.$route.name);
Expand Down Expand Up @@ -210,9 +211,12 @@
},
handleClickImport() {
const nodeIds = this.selected.map(({ id }) => id);
// Grab the source nodes from Vuex, since search should have loaded them into it
const sourceNodes = nodeIds.map(id => this.getContentNode(id));
return this.copyContentNodes({
id__in: nodeIds,
target: this.$route.params.destNodeId,
sourceNodes,
}).then(() => {
// When exiting, do not show snackbar when clearing selections
this.showSnackbar = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,22 +391,34 @@ export function deleteContentNodes(context, contentNodeIds) {

export function copyContentNode(
context,
{ id, target, position = RELATIVE_TREE_POSITIONS.LAST_CHILD, excluded_descendants = null } = {}
{
id,
target,
position = RELATIVE_TREE_POSITIONS.LAST_CHILD,
excluded_descendants = null,
sourceNode = null,
} = {}
) {
// 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, sourceNode).then(node => {
context.commit('ADD_CONTENTNODE', node);
return node;
});
}

export function copyContentNodes(
context,
{ id__in, target, position = RELATIVE_TREE_POSITIONS.LAST_CHILD }
{ id__in, target, position = RELATIVE_TREE_POSITIONS.LAST_CHILD, sourceNodes = null }
) {
return Promise.all(
id__in.map(id => context.dispatch('copyContentNode', { id, target, position }))
id__in.map(id => {
let sourceNode = null;
if (sourceNodes) {
sourceNode = sourceNodes.find(n => n.id === id);
}
return context.dispatch('copyContentNode', { id, target, position, sourceNode });
})
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import isString from 'lodash/isString';
import find from 'lodash/find';
import { ContentKindsNames } from 'shared/leUtils/ContentKinds';
import { RolesNames } from 'shared/leUtils/Roles';

export function parseNode(node, children) {
const thumbnail_encoding = JSON.parse(node.thumbnail_encoding || '{}');
const thumbnail_encoding = isString(node.thumbnail_encoding)
? JSON.parse(node.thumbnail_encoding || '{}')
: node.thumbnail_encoding || {};
const tags = Object.keys(node.tags || {});
const aggregateValues = {};
if (node.kind === ContentKindsNames.TOPIC) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,54 @@
import partition from 'lodash/partition';
import client from 'shared/client';
import urls from 'shared/urls';
import * as publicApi from 'shared/data/public';
import { NOVALUE, ChannelListTypes } from 'shared/constants';

import { Channel, SavedSearch } from 'shared/data/resources';

export function fetchResourceSearchResults(context, params) {
export async function fetchResourceSearchResults(context, params) {
params = { ...params };
delete params['last'];
params.page_size = params.page_size || 25;
params.channel_list = params.channel_list || ChannelListTypes.PUBLIC;
return client.get(window.Urls.search_list(), { params }).then(response => {
context.commit('contentNode/ADD_CONTENTNODES', response.data.results, { root: true });
return response.data;
});

const response = await client.get(urls.search_list(), { params });

// Split nodes into public and private so we can call the separate apis
const [publicNodes, privateNodes] = partition(response.data.results, node => node.public);

const privatePromise = privateNodes.length
? context.dispatch(
'contentNode/loadContentNodes',
{
id__in: privateNodes.map(node => node.id),
},
{ root: true }
)
: Promise.resolve([]);

await Promise.all([
// the loadContentNodes action already loads the nodes into vuex
privatePromise,
Promise.all(
// The public API is cached, so we can hopefully call it multiple times without
// worrying too much about performance
publicNodes.map(async node => {
const publicNode = await publicApi.getContentNode(node.node_id).catch(() => null);
if (!publicNode) {
return;
}
return publicApi.convertContentNodeResponse(node.id, node.root_id, publicNode);
})
)
.then(nodes => nodes.filter(Boolean))
.then(nodes => {
context.commit('contentNode/ADD_CONTENTNODES', nodes, { root: true });
return nodes;
}),
]);

return response.data;
}

export function loadChannels(context, params) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,10 @@ describe('ContentNode methods', () => {
it('should reject with error when attempting to set as child of itself', async () => {
parent.id = 'abc123';
await expect(
ContentNode.resolveTreeInsert('abc123', 'target', 'position', false, jest.fn())
ContentNode.resolveTreeInsert(
{ id: 'abc123', target: 'target', position: 'position', isCreate: false },
jest.fn()
)
).rejects.toThrow('Cannot set node as child of itself');
expect(resolveParent).toHaveBeenCalledWith('target', 'position');
});
Expand All @@ -307,7 +310,10 @@ describe('ContentNode methods', () => {
it('should default to appending', async () => {
const cb = jest.fn(() => Promise.resolve('results'));
await expect(
ContentNode.resolveTreeInsert('abc123', 'target', 'position', false, cb)
ContentNode.resolveTreeInsert(
{ id: 'abc123', target: 'target', position: 'position', isCreate: false },
cb
)
).resolves.toEqual('results');
expect(resolveParent).toHaveBeenCalledWith('target', 'position');
expect(treeLock).toHaveBeenCalledWith(parent.root_id, expect.any(Function));
Expand Down Expand Up @@ -340,7 +346,10 @@ describe('ContentNode methods', () => {
const cb = jest.fn(() => Promise.resolve('results'));
parent.channel_id = null;
await expect(
ContentNode.resolveTreeInsert('abc123', 'target', 'position', false, cb)
ContentNode.resolveTreeInsert(
{ id: 'abc123', target: 'target', position: 'position', isCreate: false },
cb
)
).resolves.toEqual('results');
expect(resolveParent).toHaveBeenCalledWith('target', 'position');
expect(treeLock).toHaveBeenCalledWith(parent.root_id, expect.any(Function));
Expand Down Expand Up @@ -377,7 +386,10 @@ describe('ContentNode methods', () => {
.map((_, i) => ({ id: uuid4(), lft: i, title: `Sibling ${i}` }));

await expect(
ContentNode.resolveTreeInsert('abc123', 'target', 'position', false, cb)
ContentNode.resolveTreeInsert(
{ id: 'abc123', target: 'target', position: 'position', isCreate: false },
cb
)
).resolves.toEqual('results');
expect(resolveParent).toHaveBeenCalledWith('target', 'position');
expect(treeLock).toHaveBeenCalledWith(parent.root_id, expect.any(Function));
Expand Down Expand Up @@ -413,7 +425,10 @@ describe('ContentNode methods', () => {
.fill(1)
.map((_, i) => ({ id: uuid4(), title: `Sibling ${i}` }));
await expect(
ContentNode.resolveTreeInsert('abc123', 'target', 'position', false, cb)
ContentNode.resolveTreeInsert(
{ id: 'abc123', target: 'target', position: 'position', isCreate: false },
cb
)
).rejects.toThrow('New lft value evaluated to null');
expect(resolveParent).toHaveBeenCalledWith('target', 'position');
expect(treeLock).toHaveBeenCalledWith(parent.root_id, expect.any(Function));
Expand All @@ -428,7 +443,10 @@ describe('ContentNode methods', () => {
it('should default to appending', async () => {
const cb = jest.fn(() => Promise.resolve('results'));
await expect(
ContentNode.resolveTreeInsert('abc123', 'target', 'position', true, cb)
ContentNode.resolveTreeInsert(
{ id: 'abc123', target: 'target', position: 'position', isCreate: true },
cb
)
).resolves.toEqual('results');
expect(resolveParent).toHaveBeenCalledWith('target', 'position');
expect(treeLock).toHaveBeenCalledWith(parent.root_id, expect.any(Function));
Expand Down Expand Up @@ -465,7 +483,10 @@ describe('ContentNode methods', () => {
.fill(1)
.map((_, i) => ({ id: uuid4(), title: `Sibling ${i}` }));
await expect(
ContentNode.resolveTreeInsert('abc123', 'target', 'position', true, cb)
ContentNode.resolveTreeInsert(
{ id: 'abc123', target: 'target', position: 'position', isCreate: true },
cb
)
).resolves.toEqual('results');
expect(resolveParent).toHaveBeenCalledWith('target', 'position');
expect(treeLock).toHaveBeenCalledWith(parent.root_id, expect.any(Function));
Expand Down Expand Up @@ -502,7 +523,10 @@ describe('ContentNode methods', () => {
.fill(1)
.map((_, i) => ({ id: uuid4(), title: `Sibling ${i}` }));
await expect(
ContentNode.resolveTreeInsert('abc123', 'target', 'position', true, cb)
ContentNode.resolveTreeInsert(
{ id: 'abc123', target: 'target', position: 'position', isCreate: true },
cb
)
).rejects.toThrow('New lft value evaluated to null');
expect(resolveParent).toHaveBeenCalledWith('target', 'position');
expect(treeLock).toHaveBeenCalledWith(parent.root_id, expect.any(Function));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function applyMove(change) {
}

const { key, target, position } = change;
return resource.resolveTreeInsert(key, target, position, false, data => {
return resource.resolveTreeInsert({ id: key, target, position, isCreate: false }, data => {
return transaction(change, () => {
return resource.tableMove(data);
});
Expand All @@ -97,7 +97,7 @@ function applyCopy(change) {

const { key, target, position, from_key } = change;
// copying takes the ID of the node to copy, so we use `from_key`
return resource.resolveTreeInsert(from_key, target, position, true, data => {
return resource.resolveTreeInsert({ id: from_key, target, position, isCreate: true }, data => {
return transaction(change, () => {
// Update the ID on the payload to match the received change, since isCreate=true
// would generate new IDs
Expand Down
Loading