From bdde6639a3adda91585813a495964aebe6271f15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Redrejo?=
Date: Fri, 11 Nov 2022 20:06:05 +0100
Subject: [PATCH] Adding locations
---
kolibri/core/device/serializers.py | 6 +-
.../AddStorageLocationModal.vue | 37 +++++++--
.../AddStorageServerRestartModal.vue | 36 ---------
.../DeviceSettingsPage/ServerRestartModal.vue | 75 ++++++++++++++++-
.../src/views/DeviceSettingsPage/api.js | 10 +++
.../src/views/DeviceSettingsPage/index.vue | 80 +++++++++++++------
6 files changed, 171 insertions(+), 73 deletions(-)
delete mode 100644 kolibri/plugins/device/assets/src/views/DeviceSettingsPage/AddStorageServerRestartModal.vue
diff --git a/kolibri/core/device/serializers.py b/kolibri/core/device/serializers.py
index c338f8b0ee9..ffaec369c04 100644
--- a/kolibri/core/device/serializers.py
+++ b/kolibri/core/device/serializers.py
@@ -137,9 +137,9 @@ def create(self, validated_data):
class DeviceSettingsSerializer(DeviceSerializerMixin, serializers.ModelSerializer):
extra_settings = serializers.JSONField(required=False)
- primary_storage_location = serializers.CharField(required=True)
+ primary_storage_location = serializers.CharField(required=False)
secondary_storage_locations = serializers.ListField(
- child=serializers.CharField(), required=False
+ child=serializers.CharField(required=False), required=False
)
class Meta:
@@ -182,7 +182,7 @@ def validate(self, data):
if not check_is_directory(path):
raise serializers.ValidationError(
{
- "secondary_storage_locations": "Primary storage location must be a directory"
+ "secondary_storage_locations": "Secondary storage location must be a directory"
}
)
return data
diff --git a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/AddStorageLocationModal.vue b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/AddStorageLocationModal.vue
index 94d2d1ab8d1..9d96059cb0a 100644
--- a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/AddStorageLocationModal.vue
+++ b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/AddStorageLocationModal.vue
@@ -4,7 +4,6 @@
:title="$tr('newStorageLocation')"
:submitText="coreString('continueAction')"
:cancelText="coreString('cancelAction')"
- :submitDisabled="submitDisabled"
@submit="handleSubmit"
@cancel="$emit('cancel')"
>
@@ -15,8 +14,8 @@
type="text"
:label="$tr('filePath')"
:invalid="invalidPath"
- :invalidText="$tr('error')"
- showInvalidText="true"
+ :invalidText="showError"
+ :showInvalidText="true"
@input="invalidPath = false"
/>
@@ -32,19 +31,43 @@
export default {
name: 'AddStorageLocationModal',
mixins: [commonCoreStrings],
+ props: {
+ paths: {
+ type: Array,
+ required: true,
+ },
+ },
data() {
return {
path: null,
invalidPath: false,
+ errorType: null,
};
},
+ computed: {
+ showError() {
+ if (this.errorType === 'directory') {
+ return this.$tr('errorInvalidFolder');
+ } else {
+ return this.$tr('errorExistingFolder');
+ }
+ },
+ },
methods: {
handleSubmit() {
getPathPermissions(this.path).then(permissions => {
const writable = permissions.data.writable;
+ const exists = this.paths.filter(el => el.path === this.path);
+ if (exists.length > 0) {
+ this.invalidPath = true;
+ this.errorType = 'exists';
+ return;
+ }
this.invalidPath = !permissions.data.directory;
if (permissions.data.directory) {
this.$emit('submit', this.path, writable);
+ } else {
+ this.errorType = 'directory';
}
});
},
@@ -63,8 +86,12 @@
message: 'File path',
context: 'Label for new storage location input',
},
- error: {
- message: 'Invalid path',
+ errorInvalidFolder: {
+ message: 'This is not a valid folder in the server',
+ context: 'Error text for new storage location input',
+ },
+ errorExistingFolder: {
+ message: 'This folder is already in the list of storage locations',
context: 'Error text for new storage location input',
},
},
diff --git a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/AddStorageServerRestartModal.vue b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/AddStorageServerRestartModal.vue
deleted file mode 100644
index 8de15557af5..00000000000
--- a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/AddStorageServerRestartModal.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
- {{ $tr('serverRestartDescription') }}
-
-
-
-
-
-
diff --git a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/ServerRestartModal.vue b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/ServerRestartModal.vue
index 1651850b5a4..5f63aa9e0fc 100644
--- a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/ServerRestartModal.vue
+++ b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/ServerRestartModal.vue
@@ -4,10 +4,22 @@
:title="$tr('serverRestart')"
:submitText="coreString('continueAction')"
:cancelText="coreString('cancelAction')"
- @submit="$emit('submit')"
+ @submit="handleSubmit"
@cancel="$emit('cancel')"
>
- {{ $tr('serverRestartDecription', { changedSetting: changedSetting }) }}
+
+ {{ $tr('selectedPath', { path: path.name }) }}
+
+ {{ getMessage() }}
+
+
+
+ {{ $tr('labelPrimary') }}
+
+
@@ -22,20 +34,75 @@
mixins: [commonCoreStrings],
props: {
changedSetting: {
- type: String,
+ type: String, // primary, remove, add
required: true,
},
+ path: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ confirmationChecked: false,
+ };
+ },
+ methods: {
+ getMessage() {
+ let message = '';
+ if (this.changedSetting === 'primary') {
+ message = this.$tr('newPrimaryLocationRestartDescription');
+ } else if (this.changedSetting === 'remove') {
+ message = this.$tr('removeLocationRestartDescription');
+ } else if (this.changedSetting === 'add') {
+ message = this.$tr('newLocationRestartDescription');
+ }
+
+ return message + this.$tr('serverRestartDecription');
+ },
+ handleSubmit() {
+ if (this.changedSetting === 'primary') {
+ this.$emit('submit', this.confirmationChecked);
+ } else {
+ this.$emit('submit');
+ }
+ },
},
$trs: {
serverRestart: {
message: 'Server restart',
context: 'Prompt for removing a storage location.',
},
+ newLocationRestartDescription: {
+ message: 'Server needs to restart to add a new storage location.',
+ context: 'Reason to restart the server.',
+ },
+ newPrimaryLocationRestartDescription: {
+ message: 'Changing the primary storage location will restart this server.',
+ context: 'Reason to restart the server.',
+ },
+ removeLocationRestartDescription: {
+ message: 'Removing a storage location will restart this server.',
+ context: 'Reason to restart the server.',
+ },
serverRestartDecription: {
message:
- '{changedSetting} restart this server. Anyone using Kolibri on this server right now will temporarily be unable to use it.',
+ ' Anyone using Kolibri on this server right now will temporarily be unable to use it.',
context: 'Description for restarting the server.',
},
+ selectedPath: {
+ message: 'Selected: {path}',
+ context: 'Label for the selected path.',
+ },
+ makePrimary: {
+ message: 'Make this the primary storage location',
+ context: 'Checkbox to make primary storage location.',
+ },
+ labelPrimary: {
+ message: 'Newly downloaded resources will be added to the primary storage location',
+ context: 'Label for primary storage location.',
+ },
},
};
diff --git a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/api.js b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/api.js
index 1667e567c67..92e75b9651b 100644
--- a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/api.js
+++ b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/api.js
@@ -52,3 +52,13 @@ export function getPathPermissions(path) {
params: { path },
});
}
+
+export function getPathsPermissions(paths) {
+ let pathsInfo = [];
+ for (let path of paths) {
+ getPathPermissions(path).then(permissions => {
+ pathsInfo.push({ path, writable: permissions.data.writable });
+ });
+ }
+ return pathsInfo;
+}
diff --git a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/index.vue b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/index.vue
index a759d06c725..897aeb7fae4 100644
--- a/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/index.vue
+++ b/kolibri/plugins/device/assets/src/views/DeviceSettingsPage/index.vue
@@ -151,7 +151,7 @@
{{ $tr('secondaryStorageDescription') }}
- {{ path }}
+ {{ path }} {{ isWritablePath(path) }}
{{ $tr('configureFacilitySettingsHeader') }}
-
- -
+
+
-
@@ -269,23 +270,11 @@
@submit="handleSubmit"
/>
-
-
-
-
@@ -309,11 +298,10 @@
import DeviceTopNav from '../DeviceTopNav';
import { deviceString } from '../commonDeviceStrings';
import { getFreeSpaceOnServer } from '../AvailableChannelsPage/api';
- import { getDeviceSettings, saveDeviceSettings, getDeviceURLs } from './api';
+ import { getDeviceSettings, getPathsPermissions, saveDeviceSettings, getDeviceURLs } from './api';
import PrimaryStorageLocationModal from './PrimaryStorageLocationModal';
import AddStorageLocationModal from './AddStorageLocationModal';
import RemoveStorageLocationModal from './RemoveStorageLocationModal';
- import AddStorageServerRestartModal from './AddStorageServerRestartModal';
import ServerRestartModal from './ServerRestartModal';
const SignInPageOptions = Object.freeze({
@@ -335,7 +323,6 @@
PrimaryStorageLocationModal,
AddStorageLocationModal,
RemoveStorageLocationModal,
- AddStorageServerRestartModal,
ServerRestartModal,
},
mixins: [commonCoreStrings],
@@ -353,6 +340,7 @@
meteredConnectionDownloadOptions: MeteredConnectionDownloadOptions,
primaryStorageLocation: null,
secondaryStorageLocations: [],
+ storageLocations: {},
enableAutomaticDownload: null,
allowLearnerDownloadResources: null,
setLimitForAutodownload: null,
@@ -366,6 +354,9 @@
value: null,
label: this.$tr('browserDefaultLanguage'),
},
+ restartPath: {},
+ restartSetting: null,
+ showRestartModal: false,
};
},
computed: {
@@ -475,6 +466,10 @@
secondaryStorageLocations,
extraSettings,
});
+ this.storageLocations = getPathsPermissions([
+ ...this.secondaryStorageLocations,
+ this.primaryStorageLocation,
+ ]);
});
},
methods: {
@@ -603,6 +598,8 @@
allowPeerUnlistedChannelImport: this.allowPeerUnlistedChannelImport,
allowOtherBrowsersToConnect: this.allowOtherBrowsersToConnect,
extraSettings: this.extraSettings,
+ secondaryStorageLocations: this.secondaryStorageLocations,
+ primaryStorageLocation: this.primaryStorageLocation,
})
.then(() => {
this.$store.dispatch('createSnackbar', this.$tr('saveSuccessNotification'));
@@ -625,10 +622,39 @@
handleSubmit(e) {
e.preventDefault();
},
- addStorageLocation(path, readable) {
+ addStorageLocation(path, writable) {
+ this.restartPath = {
+ path,
+ writable,
+ };
+
+ this.restartSetting = 'add';
+ this.showRestartModal = true;
this.showAddStorageLocationModal = false;
- console.log(path);
- console.log(readable);
+ this.showAddStorageServerRestartModal = true;
+ },
+
+ handleServerRestart() {
+ this.showRestartModal = false;
+ if (this.restartSetting === 'add') {
+ this.storageLocations.push(this.restartPath);
+ this.secondaryStorageLocations.push(this.restartPath.path);
+ this.handleClickSave();
+ } else if (this.restartSetting === 'remove') {
+ this.storageLocations = this.storageLocations.filter(
+ el => el.path !== this.restartPath.path
+ );
+ // TODO: remove location
+ } else if (this.restartSetting === 'primary') {
+ // TODO: set primary location
+ }
+ },
+ isWritablePath(path) {
+ const found = this.storageLocations.find(el => el.path === path);
+ if (found !== undefined && !found.writable) {
+ return this.$tr('readOnly');
+ }
+ return '';
},
},
$trs: {
@@ -800,6 +826,10 @@
message: 'No available storage',
context: 'Error text that is provided if there is not enough free storage on device',
},
+ readOnly: {
+ message: '(read-only)',
+ context: 'Label for read-only storage locations',
+ },
},
};