diff --git a/.github/workflows/deploytest.yml b/.github/workflows/deploytest.yml
index b5c3fef9a3..cb4e4a2346 100644
--- a/.github/workflows/deploytest.yml
+++ b/.github/workflows/deploytest.yml
@@ -61,7 +61,9 @@ jobs:
${{ runner.os }}-pyprod-
- name: Install pip-tools and python dependencies
run: |
- python -m pip install --upgrade pip
+ # Pin pip to 25.2 to avoid incompatibility with pip-tools and 25.3
+ # see https://github.com/jazzband/pip-tools/issues/2252
+ python -m pip install pip==25.2
pip install pip-tools
pip-sync requirements.txt
- name: Use pnpm
diff --git a/.github/workflows/pythontest.yml b/.github/workflows/pythontest.yml
index ec99bec269..5467f8a47c 100644
--- a/.github/workflows/pythontest.yml
+++ b/.github/workflows/pythontest.yml
@@ -82,7 +82,9 @@ jobs:
${{ runner.os }}-pytest-
- name: Install pip-tools and python dependencies
run: |
- python -m pip install --upgrade pip
+ # Pin pip to 25.2 to avoid incompatibility with pip-tools and 25.3
+ # see https://github.com/jazzband/pip-tools/issues/2252
+ python -m pip install pip==25.2
pip install pip-tools
pip-sync requirements.txt requirements-dev.txt
- name: Test pytest
diff --git a/Makefile b/Makefile
index a4160f6b99..002d337323 100644
--- a/Makefile
+++ b/Makefile
@@ -38,7 +38,7 @@ migrate:
# 4) Remove the management command from this `deploy-migrate` recipe
# 5) Repeat!
deploy-migrate:
- python contentcuration/manage.py set_file_duration
+ echo "Nothing to do here!"
contentnodegc:
python contentcuration/manage.py garbage_collect
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditBooleanMapModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditBooleanMapModal.vue
index a8b621bd13..93f34bc587 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditBooleanMapModal.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditBooleanMapModal.vue
@@ -95,7 +95,7 @@
},
data() {
return {
- updateDescendants: false,
+ updateDescendants: true,
error: '',
/**
* selectedValues is an object with the following structure:
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditLanguageModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditLanguageModal.vue
index 6287b14dd5..381afcf60f 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditLanguageModal.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditLanguageModal.vue
@@ -92,7 +92,7 @@
return {
selectedLanguage: '',
searchQuery: '',
- updateDescendants: false,
+ updateDescendants: true,
isMultipleNodeLanguages: false,
changed: false,
};
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditTitleDescriptionModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditTitleDescriptionModal.vue
index 3acdde0ad2..15b271664e 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditTitleDescriptionModal.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditTitleDescriptionModal.vue
@@ -98,6 +98,7 @@
id: nodeId,
title: title.trim(),
description: description.trim(),
+ checkComplete: true,
});
/* eslint-disable-next-line kolibri/vue-no-undefined-string-uses */
this.$store.dispatch('showSnackbarSimple', commonStrings.$tr('changesSaved'));
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditBooleanMapModal.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditBooleanMapModal.spec.js
index 2d4306846b..aa96754a67 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditBooleanMapModal.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditBooleanMapModal.spec.js
@@ -275,10 +275,11 @@ describe('EditBooleanMapModal', () => {
expect(wrapper.find('[data-test="update-descendants-checkbox"]').exists()).toBeFalsy();
});
- test('should call updateContentNode on success submit if the user does not check the update descendants checkbox', async () => {
+ test('should call updateContentNode on success submit if the user uncheck the update descendants checkbox', async () => {
nodes['node1'].kind = ContentKindsNames.TOPIC;
const wrapper = makeWrapper({ nodeIds: ['node1'], isDescendantsUpdatable: true });
+ wrapper.find('[data-test="update-descendants-checkbox"]').element.click();
await wrapper.vm.handleSave();
expect(contentNodeActions.updateContentNode).toHaveBeenCalledWith(expect.anything(), {
@@ -287,11 +288,10 @@ describe('EditBooleanMapModal', () => {
});
});
- test('should call updateContentNodeDescendants on success submit if the user checks the descendants checkbox', async () => {
+ test('should call updateContentNodeDescendants on success submit if the user does not uncheck the update descendants checkbox', async () => {
nodes['node1'].kind = ContentKindsNames.TOPIC;
const wrapper = makeWrapper({ nodeIds: ['node1'], isDescendantsUpdatable: true });
- wrapper.find('[data-test="update-descendants-checkbox"]').element.click();
await wrapper.vm.handleSave();
expect(contentNodeActions.updateContentNodeDescendants).toHaveBeenCalledWith(
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditLanguageModal.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditLanguageModal.spec.js
index c9716d55da..2c2cb791ff 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditLanguageModal.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditLanguageModal.spec.js
@@ -220,8 +220,8 @@ describe('EditLanguageModal', () => {
});
describe('topic nodes present', () => {
- it('should display the checkbox to apply change to descendants if a topic is present', () => {
- [wrapper] = makeWrapper(['test-en-topic', 'test-en-res']);
+ test('should display a selected checkbox to apply change to descendants if a topic is present', () => {
+ const [wrapper] = makeWrapper(['test-en-topic', 'test-en-res']);
expect(
wrapper.findComponent('[data-test="update-descendants-checkbox"]').exists(),
@@ -236,30 +236,33 @@ describe('EditLanguageModal', () => {
).toBeFalsy();
});
- it('should call updateContentNode with the right language on success submit if the user does not check the checkbox', async () => {
- [wrapper, mocks] = makeWrapper(['test-en-topic', 'test-en-res']);
+ test('should call updateContentNodeDescendants with the right language on success submit by default', async () => {
+ const [wrapper, mocks] = makeWrapper(['test-en-topic', 'test-en-res']);
await chooseLanguage(wrapper, 'es');
await wrapper.vm.handleSave();
- await wrapper.vm.$nextTick();
- expect(mocks.updateContentNode).toHaveBeenCalledWith({
+ expect(mocks.updateContentNodeDescendants).toHaveBeenCalledWith({
id: 'test-en-topic',
language: 'es',
});
});
- it('should call updateContentNodeDescendants with the right language on success submit if the user checks the checkbox', async () => {
- [wrapper, mocks] = makeWrapper(['test-en-topic', 'test-en-res']);
+ test('should call updateContentNode with the right language on success submit if the user unchecks check the checkbox', async () => {
+ const [wrapper, mocks] = makeWrapper(['test-en-topic', 'test-en-res']);
await chooseLanguage(wrapper, 'es');
- wrapper.findComponent('[data-test="update-descendants-checkbox"]').vm.$emit('change', true);
+
+ // Uncheck the descendants checkbox
+ const descendantsCheckbox = wrapper.findComponent(
+ '[data-test="update-descendants-checkbox"]',
+ );
+ descendantsCheckbox.vm.$emit('change', false);
await wrapper.vm.$nextTick();
- expect(wrapper.vm.updateDescendants).toBe(true);
+
await wrapper.vm.handleSave();
- await wrapper.vm.$nextTick();
- expect(mocks.updateContentNodeDescendants).toHaveBeenCalledWith({
+ expect(mocks.updateContentNode).toHaveBeenCalledWith({
id: 'test-en-topic',
language: 'es',
});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditTitleDescriptionModal.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditTitleDescriptionModal.spec.js
index 464a8e9026..632f364fe1 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditTitleDescriptionModal.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditTitleDescriptionModal.spec.js
@@ -33,9 +33,7 @@ describe('EditTitleDescriptionModal', () => {
},
},
}),
- propsData: {
- nodeId,
- },
+ propsData: { nodeId },
});
updateContentNode = jest.spyOn(wrapper.vm, 'updateContentNode').mockImplementation(() => {});
@@ -70,7 +68,8 @@ describe('EditTitleDescriptionModal', () => {
expect(updateContentNode).toHaveBeenCalledWith({
id: nodeId,
title: newTitle,
- description: newDescription,
+ description: newDescription ?? '',
+ checkComplete: true,
});
});
@@ -80,11 +79,11 @@ describe('EditTitleDescriptionModal', () => {
descriptionInput.vm.$emit('input', '');
modal.vm.$emit('submit');
-
expect(updateContentNode).toHaveBeenCalledWith({
id: nodeId,
title: newTitle,
description: '',
+ checkComplete: true,
});
});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ResourcePanel.vue b/contentcuration/contentcuration/frontend/channelEdit/components/ResourcePanel.vue
index 674bcfa160..e8616b3c1e 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/ResourcePanel.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/ResourcePanel.vue
@@ -74,7 +74,8 @@
slider-color="primary"
>
@@ -86,7 +87,8 @@
/>
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
index 3002797c94..d7e6021ee9 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
@@ -499,6 +499,7 @@
} from 'shared/constants';
import { constantsTranslationMixin, metadataTranslationMixin } from 'shared/mixins';
import { crossComponentTranslator } from 'shared/i18n';
+ import { LanguagesNames } from 'shared/leUtils/Languages';
function getValueFromResults(results) {
if (results.length === 0) {
@@ -715,7 +716,17 @@
},
},
role: generateGetterSetter('role_visibility'),
- language: generateGetterSetter('language'),
+ language: {
+ get() {
+ const value = this.getValueFromNodes('language');
+ return value === nonUniqueValue ? LanguagesNames.MUL : value;
+ },
+ set(value) {
+ if (!(value === LanguagesNames.MUL && this.language === LanguagesNames.MUL)) {
+ this.update({ language: value });
+ }
+ },
+ },
accessibility: generateNestedNodesGetterSetter('accessibility_labels'),
contentLevel: generateNestedNodesGetterSetterObject('grade_levels'),
resourcesNeeded: generateNestedNodesGetterSetterObject('learner_needs'),
diff --git a/contentcuration/contentcuration/frontend/channelEdit/views/ImportFromChannels/SearchResultsList.vue b/contentcuration/contentcuration/frontend/channelEdit/views/ImportFromChannels/SearchResultsList.vue
index 4f95c2cd99..2862ff2146 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/views/ImportFromChannels/SearchResultsList.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/views/ImportFromChannels/SearchResultsList.vue
@@ -3,13 +3,22 @@
-
-
-
-
-
-
-
+
+
+
+
+
@@ -105,8 +114,8 @@
-
-
+
+
diff --git a/contentcuration/contentcuration/frontend/shared/views/__tests__/languageDropdown.spec.js b/contentcuration/contentcuration/frontend/shared/views/__tests__/languageDropdown.spec.js
index f478ebcd9b..8bdcd165fd 100644
--- a/contentcuration/contentcuration/frontend/shared/views/__tests__/languageDropdown.spec.js
+++ b/contentcuration/contentcuration/frontend/shared/views/__tests__/languageDropdown.spec.js
@@ -1,4 +1,4 @@
-import { mount } from '@vue/test-utils';
+import { mount, shallowMount } from '@vue/test-utils';
import LanguageDropdown from '../LanguageDropdown.vue';
import TestForm from './TestForm.vue';
import { LanguagesList } from 'shared/leUtils/Languages';
@@ -61,4 +61,24 @@ describe('languageDropdown', () => {
await wrapper.vm.$nextTick();
expect(wrapper.find('.error--text').exists()).toBe(true);
});
+
+ it('returns formatted language text when native_name is present', () => {
+ const wrapper = shallowMount(LanguageDropdown, {
+ mocks: {
+ $tr: (key, params) => `${params.language} (${params.code})`,
+ },
+ });
+ const item = { native_name: 'Español,Spanish', id: 'es' };
+ expect(wrapper.vm.languageText(item)).toBe('Español (es)');
+ });
+
+ it('returns formatted language text when native_name is an empty string', () => {
+ const wrapper = shallowMount(LanguageDropdown, {
+ mocks: {
+ $tr: (key, params) => `${params.language} (${params.code})`,
+ },
+ });
+ const item = { native_name: '', id: 'de' };
+ expect(wrapper.vm.languageText(item)).toBe(' (de)');
+ });
});