diff --git a/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/SelectDeviceForm.vue b/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/SelectDeviceForm.vue index 4c94ab43d4f..1fe5d7cdfec 100644 --- a/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/SelectDeviceForm.vue +++ b/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/SelectDeviceForm.vue @@ -163,12 +163,17 @@ deviceFilters.push(useDeviceChannelFilter({ id: props.filterByChannelId })); } - if (props.filterByFacilityId !== null || props.filterByFacilityCanSignUp !== null) { + if ( + props.filterByFacilityId !== null || + props.filterByFacilityCanSignUp !== null || + props.filterByOnMyOwnFacility !== null + ) { apiParams.subset_of_users_device = false; deviceFilters.push( useDeviceFacilityFilter({ id: props.filterByFacilityId, learner_can_sign_up: props.filterByFacilityCanSignUp, + on_my_own_setup: props.filterByOnMyOwnFacility, }) ); } @@ -243,6 +248,12 @@ type: Boolean, default: null, }, + // In the setup wizard, to exclude importiing facilities that are "On My Own" + // eslint-disable-next-line kolibri/vue-no-unused-properties + filterByOnMyOwnFacility: { + type: Boolean, + default: null, + }, // If an ID is provided, that device's radio button will be automatically selected selectedId: { type: String, diff --git a/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/index.vue b/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/index.vue index 1e95101db3a..8ac5d1f0731 100644 --- a/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/index.vue +++ b/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/index.vue @@ -12,6 +12,7 @@ :filterByFacilityId="filterByFacilityId" :filterLODAvailable="filterLODAvailable" :filterByFacilityCanSignUp="filterByFacilityCanSignUp" + :filterByOnMyOwnFacility="filterByOnMyOwnFacility" :selectedId="addedAddressId" :formDisabled="$attrs.selectAddressDisabled" @click_add_address="goToAddAddress" @@ -55,6 +56,11 @@ type: Boolean, default: null, }, + // When looking for facilities to import in the setup wizard + filterByOnMyOwnFacility: { + type: Boolean, + default: null, + }, }, data() { return { diff --git a/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/useDevices.js b/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/useDevices.js index 14ff55cc022..70842579cb3 100644 --- a/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/useDevices.js +++ b/kolibri/core/assets/src/views/sync/SelectDeviceModalGroup/useDevices.js @@ -172,9 +172,14 @@ function useAsyncDeviceFilter(filterFunction) { * Produces a function that resolves with a boolean for a device that has the specified facility * @param {string|null} [id] * @param {bool|null} [learner_can_sign_up] + * @param {bool|null} [on_my_own_setup] * @return {function(NetworkLocation): Promise} */ -export function useDeviceFacilityFilter({ id = null, learner_can_sign_up = null }) { +export function useDeviceFacilityFilter({ + id = null, + learner_can_sign_up = null, + on_my_own_setup = null, +}) { const filters = {}; // If `id` is an empty string, we don't want to filter by that @@ -186,6 +191,10 @@ export function useDeviceFacilityFilter({ id = null, learner_can_sign_up = null filters.learner_can_sign_up = learner_can_sign_up; } + if (on_my_own_setup !== null) { + filters.on_my_own_setup = on_my_own_setup; + } + if (Object.keys(filters).length === 0) { return () => Promise.resolve(true); } diff --git a/kolibri/core/auth/serializers.py b/kolibri/core/auth/serializers.py index eae42791e4d..26918467f27 100644 --- a/kolibri/core/auth/serializers.py +++ b/kolibri/core/auth/serializers.py @@ -170,6 +170,7 @@ class Meta: class PublicFacilitySerializer(serializers.ModelSerializer): learner_can_login_with_no_password = serializers.SerializerMethodField() learner_can_sign_up = serializers.SerializerMethodField() + on_my_own_setup = serializers.SerializerMethodField() def get_learner_can_login_with_no_password(self, instance): return instance.dataset.learner_can_login_with_no_password @@ -177,6 +178,11 @@ def get_learner_can_login_with_no_password(self, instance): def get_learner_can_sign_up(self, instance): return instance.dataset.learner_can_sign_up + def get_on_my_own_setup(self, instance): + if instance.dataset.extra_fields is not None: + return instance.dataset.extra_fields.get("on_my_own_setup", False) + return False + class Meta: model = Facility fields = ( @@ -185,6 +191,7 @@ class Meta: "name", "learner_can_login_with_no_password", "learner_can_sign_up", + "on_my_own_setup", ) diff --git a/kolibri/plugins/device/assets/src/views/PostSetupModalGroup.vue b/kolibri/plugins/device/assets/src/views/PostSetupModalGroup.vue index 49f8d679d65..c797d945537 100644 --- a/kolibri/plugins/device/assets/src/views/PostSetupModalGroup.vue +++ b/kolibri/plugins/device/assets/src/views/PostSetupModalGroup.vue @@ -41,6 +41,8 @@ import WelcomeModal from './WelcomeModal'; import PermissionsChangeModal from './PermissionsChangeModal'; + const facilityImported = 'FACILITY_IS_IMPORTED'; + const Steps = Object.freeze({ WELCOME: 'WELCOME', PERMISSIONS_CHANGE: 'PERMISSIONS_CHANGE', @@ -70,12 +72,9 @@ }, computed: { ...mapGetters(['isUserLoggedIn']), - // Assume that if first facility has non-null 'last_successful_sync' - // field, then it was imported in Setup Wizard. - // This used to determine Select Source workflow to enter into importedFacility() { const [facility] = this.$store.state.core.facilities; - if (facility && facility.last_successful_sync !== null) { + if (facility && window.sessionStorage.getItem(facilityImported) === 'true') { return facility; } return null; diff --git a/kolibri/plugins/setup_wizard/assets/src/machines/wizardMachine.js b/kolibri/plugins/setup_wizard/assets/src/machines/wizardMachine.js index 6e76e19e385..4fcc0caf3da 100644 --- a/kolibri/plugins/setup_wizard/assets/src/machines/wizardMachine.js +++ b/kolibri/plugins/setup_wizard/assets/src/machines/wizardMachine.js @@ -74,6 +74,7 @@ const initialContext = { importedUsers: [], firstImportedLodUser: null, facilitiesOnDeviceCount: null, + isImportedFacility: false, }; export const wizardMachine = createMachine( @@ -307,6 +308,7 @@ export const wizardMachine = createMachine( on: { BACK: 'selectSuperAdminAccountForm', }, + exit: 'setImportedFacility', }, }, // Listener on the importFacility state; typically this would be above `states` but @@ -537,6 +539,11 @@ export const wizardMachine = createMachine( * This effectively resets the machine's state */ resetContext: assign(initialContext), + setImportedFacility: assign({ + isImportedFacility: () => { + return true; + }, + }), }, guards: { // Functions used to return a true/false value. When the functions are called, they are passed diff --git a/kolibri/plugins/setup_wizard/assets/src/views/SelectSuperAdminAccountForm.vue b/kolibri/plugins/setup_wizard/assets/src/views/SelectSuperAdminAccountForm.vue index 303a18452a6..150a6c1d6cb 100644 --- a/kolibri/plugins/setup_wizard/assets/src/views/SelectSuperAdminAccountForm.vue +++ b/kolibri/plugins/setup_wizard/assets/src/views/SelectSuperAdminAccountForm.vue @@ -152,17 +152,17 @@ }, resetFormAndRefocus() { this.shouldValidate = false; - if (this.$refs.password) { + if (this.$refs.passwordTextbox) { // If password was set to the facility.password in handler if (this.selected.label !== this.facility.username) { - this.$refs.password.resetAndFocus(); + this.$refs.passwordTextbox.resetAndFocus(); } } }, handleClickNextImportedUser() { this.error = false; - if (!this.passwordValid) { - this.$refs.password.focus(); + if (!this.passwordValid && 'passwordTextbox' in this.$refs) { + this.$refs.passwordTextbox.focus(); return; } return FacilityImportResource.grantsuperuserpermissions({ diff --git a/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/SetUpLearningFacilityForm.vue b/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/SetUpLearningFacilityForm.vue index d8c59ef38af..6d0ed6e5349 100644 --- a/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/SetUpLearningFacilityForm.vue +++ b/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/SetUpLearningFacilityForm.vue @@ -19,6 +19,7 @@ /> diff --git a/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/SettingUpKolibri.vue b/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/SettingUpKolibri.vue index 8dd00728852..fe3716d2746 100644 --- a/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/SettingUpKolibri.vue +++ b/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/SettingUpKolibri.vue @@ -194,7 +194,12 @@ }) .then(() => { const welcomeDismissalKey = 'DEVICE_WELCOME_MODAL_DISMISSED'; + const facilityImported = 'FACILITY_IS_IMPORTED'; window.sessionStorage.setItem(welcomeDismissalKey, false); + window.sessionStorage.setItem( + facilityImported, + this.wizardContext('isImportedFacility') + ); Lockr.rm('savedState'); // Clear out saved state machine redirectBrowser();