diff --git a/kolibri/core/assets/src/constants.js b/kolibri/core/assets/src/constants.js index f6796739d4e..589c67a7f80 100644 --- a/kolibri/core/assets/src/constants.js +++ b/kolibri/core/assets/src/constants.js @@ -186,3 +186,10 @@ export const ApplicationTypes = { KOLIBRI: 'kolibri', STUDIO: 'studio', }; + +// aliasing 'informal' to 'personal' since it's how we talk about it +export const Presets = Object.freeze({ + PERSONAL: 'informal', + FORMAL: 'formal', + NONFORMAL: 'nonformal', +}); diff --git a/kolibri/core/assets/src/mixins/notificationStrings.js b/kolibri/core/assets/src/mixins/notificationStrings.js index aa8c6420f3e..b6106798f15 100644 --- a/kolibri/core/assets/src/mixins/notificationStrings.js +++ b/kolibri/core/assets/src/mixins/notificationStrings.js @@ -139,6 +139,10 @@ export default createTranslator('NotificationStrings', { message: 'Device not removed', context: 'Snackbar message when a device fails to be removed from he sync schedule', }, + newLearningFacilityCreated: { + message: 'New learning facility created', + context: 'Snackbar message when a new facility created', + }, // TODO move more messages into this namespace: // - "Quiz started" // - "Quiz Ended" diff --git a/kolibri/core/auth/api.py b/kolibri/core/auth/api.py index d1dd722c1ee..5a3cb865582 100644 --- a/kolibri/core/auth/api.py +++ b/kolibri/core/auth/api.py @@ -60,6 +60,7 @@ from .models import Membership from .models import Role from .serializers import ClassroomSerializer +from .serializers import CreateFacilitySerializer from .serializers import ExtraFieldsSerializer from .serializers import FacilityDatasetSerializer from .serializers import FacilitySerializer @@ -74,6 +75,7 @@ from kolibri.core.auth.constants.demographics import NOT_SPECIFIED from kolibri.core.auth.permissions.general import _user_is_admin_for_own_facility from kolibri.core.auth.permissions.general import DenyAll +from kolibri.core.device.permissions import IsSuperuser from kolibri.core.device.utils import allow_guest_access from kolibri.core.device.utils import allow_other_browsers_to_connect from kolibri.core.device.utils import valid_app_key_on_request @@ -621,6 +623,13 @@ def annotate_queryset(self, queryset): ) ) + @decorators.action(methods=["post"], detail=False, permission_classes=[IsSuperuser]) + def create_facility(self, request): + serializer = CreateFacilitySerializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response() + class PublicFacilityViewSet(viewsets.ReadOnlyModelViewSet): queryset = Facility.objects.all() diff --git a/kolibri/core/auth/serializers.py b/kolibri/core/auth/serializers.py index 4c9f8fd72bc..d9f57df98fd 100644 --- a/kolibri/core/auth/serializers.py +++ b/kolibri/core/auth/serializers.py @@ -2,10 +2,15 @@ from __future__ import print_function from __future__ import unicode_literals +import logging + from django.core.validators import MinLengthValidator +from django.db import transaction from rest_framework import serializers +from rest_framework.exceptions import ParseError from rest_framework.validators import UniqueTogetherValidator +from .constants import facility_presets from .errors import IncompatibleDeviceSettingError from .errors import InvalidCollectionHierarchy from .errors import InvalidMembershipError @@ -20,6 +25,9 @@ from kolibri.core.auth.constants.demographics import NOT_SPECIFIED +logger = logging.getLogger(__name__) + + class RoleSerializer(serializers.ModelSerializer): class Meta: model = Role @@ -137,6 +145,27 @@ class Meta: fields = ("id", "name") +class CreateFacilitySerializer(serializers.ModelSerializer): + preset = serializers.ChoiceField(choices=facility_presets.choices) + + class Meta: + model = Facility + fields = ("id", "name", "preset") + + def create(self, validated_data): + preset = validated_data.get("preset") + name = validated_data.get("name") + with transaction.atomic(): + try: + facility_dataset = FacilityDataset.objects.create(preset=preset) + facility = Facility.objects.create(name=name, dataset=facility_dataset) + facility.dataset.reset_to_default_settings(preset) + except Exception as e: + logger.error("Error occured while creating facility: %s", str(e)) + raise ParseError("Error occured while creating facility") + return facility + + class PublicFacilitySerializer(serializers.ModelSerializer): learner_can_login_with_no_password = serializers.SerializerMethodField() learner_can_sign_up = serializers.SerializerMethodField() diff --git a/kolibri/plugins/device/assets/src/constants.js b/kolibri/plugins/device/assets/src/constants.js index fd49e1efe3c..bd9057991a1 100644 --- a/kolibri/plugins/device/assets/src/constants.js +++ b/kolibri/plugins/device/assets/src/constants.js @@ -71,3 +71,7 @@ export const MeteredConnectionDownloadOptions = { DISALLOW_DOWNLOAD_ON_METERED_CONNECTION: 'DISALLOW_DOWNLOAD_ON_METERED_CONNECTION', ALLOW_DOWNLOAD_ON_METERED_CONNECTION: 'ALLOW_DOWNLOAD_ON_METERED_CONNECTION', }; + +export const ImportFacility = 'import_facility'; + +export const CreateNewFacility = 'create_new_facility'; diff --git a/kolibri/plugins/device/assets/src/views/FacilitiesPage/CreateNewFacilityModal.vue b/kolibri/plugins/device/assets/src/views/FacilitiesPage/CreateNewFacilityModal.vue new file mode 100644 index 00000000000..ad6a8a394c2 --- /dev/null +++ b/kolibri/plugins/device/assets/src/views/FacilitiesPage/CreateNewFacilityModal.vue @@ -0,0 +1,122 @@ + + + + + + + diff --git a/kolibri/plugins/device/assets/src/views/FacilitiesPage/api.js b/kolibri/plugins/device/assets/src/views/FacilitiesPage/api.js new file mode 100644 index 00000000000..d8133684cad --- /dev/null +++ b/kolibri/plugins/device/assets/src/views/FacilitiesPage/api.js @@ -0,0 +1,12 @@ +import client from 'kolibri.client'; +import urls from 'kolibri.urls'; + +const url = urls['kolibri:core:facility-create-facility'](); + +export function createFacility(payload) { + return client({ + url, + method: 'POST', + data: payload, + }); +} diff --git a/kolibri/plugins/device/assets/src/views/FacilitiesPage/index.vue b/kolibri/plugins/device/assets/src/views/FacilitiesPage/index.vue index 6e6b94407fe..d518550175d 100644 --- a/kolibri/plugins/device/assets/src/views/FacilitiesPage/index.vue +++ b/kolibri/plugins/device/assets/src/views/FacilitiesPage/index.vue @@ -14,11 +14,18 @@ @click="showSyncAllModal = true" /> + > + + @@ -144,6 +151,11 @@ @success="handleStartImportSuccess" @cancel="showImportModal = false" /> +