@kalisio/kdk 2.1.9 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.travis.test.sh +42 -10
- package/README.md +2 -2
- package/core/api/application.js +7 -2
- package/core/api/authentication.js +17 -1
- package/core/api/db.js +7 -2
- package/core/api/hooks/hooks.authentication.js +4 -2
- package/core/api/hooks/hooks.authorisations.js +12 -2
- package/core/api/hooks/hooks.model.js +5 -5
- package/core/api/hooks/hooks.organisations.js +0 -4
- package/core/api/services/account/account.hooks.js +10 -6
- package/core/api/services/account/account.service.js +1 -1
- package/{map/api/services/geocoder/geocoder.hooks.js → core/api/services/import-export/import-export.hooks.js} +7 -5
- package/core/api/services/import-export/import-export.service.js +11 -0
- package/core/api/services/index.js +13 -1
- package/core/api/services/users/users.hooks.js +2 -3
- package/core/client/api.js +16 -14
- package/core/client/capabilities.js +6 -2
- package/core/client/components/KContent.vue +11 -1
- package/core/client/components/KDialog.vue +17 -15
- package/core/client/components/KModal.vue +1 -1
- package/core/client/components/KSponsor.vue +1 -1
- package/core/client/components/KTextArea.vue +5 -1
- package/core/client/components/app/KAbout.vue +1 -2
- package/core/client/components/app/KWelcome.vue +3 -5
- package/core/client/components/chart/KTimeSeriesChart.vue +24 -37
- package/core/client/components/collection/KColumn.vue +20 -17
- package/core/client/components/editor/KModalEditor.vue +0 -2
- package/core/client/components/form/KChipsField.vue +12 -2
- package/core/client/components/form/KColorField.vue +12 -2
- package/core/client/components/form/KColorScaleField.vue +12 -2
- package/core/client/components/form/KDateTimeRangeField.vue +12 -2
- package/core/client/components/form/KDatetimeField.vue +12 -2
- package/core/client/components/form/KEmailField.vue +12 -2
- package/core/client/components/form/KFileField.vue +12 -2
- package/core/client/components/form/KForm.vue +47 -9
- package/core/client/components/form/KIconField.vue +12 -2
- package/core/client/components/form/KItemField.vue +25 -4
- package/core/client/components/form/KNumberField.vue +12 -2
- package/core/client/components/form/KOptionsField.vue +12 -2
- package/core/client/components/form/KPasswordField.vue +12 -2
- package/core/client/components/form/KPhoneField.vue +13 -3
- package/core/client/components/form/KPropertyItemField.vue +12 -2
- package/core/client/components/form/KResolutionField.vue +126 -0
- package/core/client/components/form/KRoleField.vue +12 -2
- package/core/client/components/form/KSelectField.vue +14 -4
- package/core/client/components/form/KTextField.vue +12 -2
- package/core/client/components/form/KTextareaField.vue +13 -3
- package/core/client/components/form/KToggleField.vue +12 -2
- package/core/client/components/form/KTokenField.vue +12 -2
- package/core/client/components/form/KUnitField.vue +12 -2
- package/core/client/components/form/KUrlField.vue +12 -2
- package/core/client/components/input/KIconChooser.vue +10 -12
- package/core/client/components/input/KPalette.vue +2 -1
- package/core/client/components/layout/KPage.vue +5 -4
- package/core/client/components/layout/KWindow.vue +10 -10
- package/core/client/components/media/KColorScale.vue +25 -19
- package/core/client/components/media/KImageViewer.vue +57 -33
- package/core/client/components/media/KShape.vue +14 -103
- package/core/client/components/screen/KRegisterScreen.vue +0 -1
- package/core/client/components/screen/KScreenFooter.vue +0 -18
- package/core/client/components/team/KAddMember.vue +16 -22
- package/core/client/components/team/KGroupsActivity.vue +14 -0
- package/core/client/components/team/KMembersActivity.vue +12 -0
- package/core/client/components/team/KTagsActivity.vue +14 -0
- package/core/client/components/time/KDateTime.vue +23 -7
- package/core/client/components/time/KTimeControl.vue +142 -0
- package/core/client/components/tool/KExportTool.vue +57 -0
- package/core/client/composables/collection.js +0 -1
- package/core/client/composables/pwa.js +0 -1
- package/core/client/composables/schema.js +1 -1
- package/core/client/composables/session.js +30 -6
- package/core/client/exporter.js +147 -0
- package/core/client/i18n/core_en.json +91 -23
- package/core/client/i18n/core_fr.json +92 -23
- package/core/client/index.js +3 -0
- package/core/client/layout.js +34 -14
- package/core/client/local-storage.js +8 -6
- package/core/client/mixins/index.js +0 -1
- package/core/client/mixins/mixin.base-field.js +24 -2
- package/core/client/mixins/mixin.object-proxy.js +0 -1
- package/core/client/search.js +2 -1
- package/core/client/services/index.js +2 -1
- package/core/client/services/local-settings.service.js +4 -4
- package/core/client/theme.js +3 -3
- package/core/client/time.js +4 -0
- package/core/client/units.js +150 -5
- package/core/client/utils/index.js +13 -6
- package/core/client/utils/utils.account.js +1 -1
- package/core/client/utils/utils.colors.js +43 -0
- package/core/client/utils/utils.platform.js +0 -1
- package/core/client/utils/utils.pwa.js +14 -14
- package/core/client/utils/utils.session.js +1 -1
- package/core/client/utils/utils.shapes.js +270 -0
- package/core/client/utils/utils.time.js +37 -0
- package/core/common/permissions.js +3 -0
- package/core/common/schemas/settings.update.json +50 -29
- package/extras/css/core.variables.scss +3 -1
- package/extras/icons/wind-speed-0.svg +8 -0
- package/extras/icons/wind-speed-10.svg +8 -0
- package/extras/icons/wind-speed-100.svg +12 -0
- package/extras/icons/wind-speed-105.svg +13 -0
- package/extras/icons/wind-speed-15.svg +9 -0
- package/extras/icons/wind-speed-20.svg +9 -0
- package/extras/icons/wind-speed-25.svg +10 -0
- package/extras/icons/wind-speed-30.svg +10 -0
- package/extras/icons/wind-speed-35.svg +11 -0
- package/extras/icons/wind-speed-40.svg +11 -0
- package/extras/icons/wind-speed-45.svg +12 -0
- package/extras/icons/wind-speed-5.svg +9 -0
- package/extras/icons/wind-speed-50.svg +9 -0
- package/extras/icons/wind-speed-55.svg +10 -0
- package/extras/icons/wind-speed-60.svg +10 -0
- package/extras/icons/wind-speed-65.svg +11 -0
- package/extras/icons/wind-speed-70.svg +11 -0
- package/extras/icons/wind-speed-75.svg +12 -0
- package/extras/icons/wind-speed-80.svg +12 -0
- package/extras/icons/wind-speed-85.svg +13 -0
- package/extras/icons/wind-speed-90.svg +13 -0
- package/extras/icons/wind-speed-95.svg +14 -0
- package/extras/tours/map/navigation-bar.js +17 -15
- package/extras/tours/map/timeline.js +33 -33
- package/map/api/config/categories.cjs +4 -1
- package/map/api/hooks/hooks.catalog.js +39 -0
- package/map/api/hooks/hooks.features.js +23 -3
- package/map/api/hooks/hooks.query.js +65 -21
- package/map/api/models/projects.model.mongodb.js +8 -0
- package/map/api/services/catalog/catalog.hooks.js +5 -3
- package/map/api/services/features/features.hooks.js +18 -6
- package/map/api/services/index.js +22 -6
- package/map/api/services/projects/projects.hooks.js +118 -0
- package/map/client/capture.js +16 -0
- package/map/client/cesium/utils/index.js +4 -0
- package/map/client/cesium/utils/utils.events.js +30 -0
- package/map/client/cesium/utils/utils.features.js +8 -0
- package/map/client/cesium/utils/utils.popup.js +17 -0
- package/map/client/cesium/utils/utils.style.js +137 -0
- package/map/client/components/KCapture.vue +50 -0
- package/map/client/components/KCaptureTextArea.vue +53 -0
- package/map/client/components/KCompass.vue +2 -2
- package/map/client/components/KFeaturesChart.vue +1 -1
- package/map/client/components/KFeaturesFilter.vue +2 -2
- package/map/client/components/KLayerStyleForm.vue +288 -454
- package/map/client/components/KLevelSlider.vue +1 -1
- package/map/client/components/KNorth.vue +31 -0
- package/map/client/components/KProjectMenu.vue +88 -0
- package/map/client/components/KTimezoneMap.vue +36 -24
- package/map/client/components/catalog/KAddLayer.vue +3 -4
- package/map/client/components/catalog/KConnectLayer.vue +20 -4
- package/map/client/components/catalog/KCreateLayer.vue +1 -2
- package/map/client/components/catalog/KCreateProject.vue +100 -0
- package/map/client/components/catalog/KCreateView.vue +25 -2
- package/map/client/components/catalog/KLayersPanel.vue +24 -27
- package/map/client/components/catalog/KLayersSelector.vue +1 -1
- package/map/client/components/catalog/KProjectEditor.vue +91 -0
- package/map/client/components/catalog/KProjectManager.vue +60 -0
- package/map/client/components/catalog/KProjectSelector.vue +38 -0
- package/map/client/components/catalog/KProjectsPanel.vue +153 -0
- package/map/client/components/catalog/KSelectLayers.vue +96 -0
- package/map/client/components/catalog/KSelectViews.vue +96 -0
- package/map/client/components/catalog/KViewsPanel.vue +66 -30
- package/map/client/components/form/KDirectionField.vue +24 -5
- package/map/client/components/form/KLayerCategoryField.vue +12 -2
- package/map/client/components/form/KLocationField.vue +20 -5
- package/map/client/components/form/KOwsLayerField.vue +12 -2
- package/map/client/components/form/KOwsServiceField.vue +12 -2
- package/map/client/components/form/KSelectLayersField.vue +159 -0
- package/map/client/components/form/KSelectViewsField.vue +121 -0
- package/map/client/components/form/KTimezoneField.vue +24 -17
- package/map/client/components/legend/KColorScaleLegend.vue +6 -2
- package/map/client/components/legend/KLayerLegend.vue +71 -0
- package/map/client/components/legend/KLegend.vue +54 -51
- package/map/client/components/legend/KLegendRenderer.vue +5 -3
- package/map/client/components/legend/KSymbolsLegend.vue +12 -10
- package/map/client/components/legend/KVariablesLegend.vue +78 -0
- package/map/client/components/location/KGeocodersFilter.vue +2 -4
- package/map/client/components/location/KLocationCardSection.vue +8 -4
- package/map/client/components/location/KLocationMap.vue +48 -17
- package/map/client/components/location/KLocationSearch.vue +13 -3
- package/map/client/components/tools/KSearchTool.vue +17 -12
- package/map/client/components/widget/KElevationProfile.vue +16 -19
- package/map/client/components/widget/KMapillaryViewer.vue +21 -22
- package/map/client/components/widget/KTimeSeries.vue +35 -29
- package/map/client/composables/activity.js +15 -2
- package/map/client/composables/catalog.js +81 -0
- package/map/client/composables/highlight.js +45 -30
- package/map/client/composables/index.js +2 -0
- package/map/client/composables/location.js +25 -18
- package/map/client/composables/probe.js +4 -1
- package/map/client/composables/project.js +122 -0
- package/map/client/composables/weather.js +3 -3
- package/map/client/geolocation.js +1 -1
- package/map/client/globe.js +2 -0
- package/map/client/i18n/map_en.json +127 -76
- package/map/client/i18n/map_fr.json +128 -72
- package/map/client/index.js +3 -0
- package/map/client/init.js +17 -0
- package/map/client/leaflet/GSMaPLayer.js +16 -17
- package/map/client/leaflet/ShapeMarker.js +51 -0
- package/map/client/leaflet/TiledFeatureLayer.js +39 -9
- package/map/client/leaflet/TiledMeshLayer.js +13 -15
- package/map/client/leaflet/TiledWindLayer.js +6 -10
- package/map/client/leaflet/utils/index.js +4 -0
- package/map/client/leaflet/utils/utils.events.js +41 -0
- package/map/client/leaflet/utils/utils.popup.js +21 -0
- package/map/client/leaflet/utils/utils.style.js +195 -0
- package/map/client/leaflet/utils/utils.tiles.js +87 -0
- package/map/client/map.js +2 -0
- package/map/client/mixins/globe/mixin.base-globe.js +39 -18
- package/map/client/mixins/globe/mixin.geojson-layers.js +139 -69
- package/map/client/mixins/globe/mixin.popup.js +2 -1
- package/map/client/mixins/globe/mixin.style.js +6 -4
- package/map/client/mixins/globe/mixin.tooltip.js +8 -3
- package/map/client/mixins/map/mixin.base-map.js +53 -28
- package/map/client/mixins/map/mixin.edit-layers.js +15 -15
- package/map/client/mixins/map/mixin.forecast-layers.js +3 -1
- package/map/client/mixins/map/mixin.geojson-layers.js +60 -20
- package/map/client/mixins/map/mixin.georaster-layers.js +4 -11
- package/map/client/mixins/map/mixin.heatmap-layers.js +1 -1
- package/map/client/mixins/map/mixin.popup.js +2 -1
- package/map/client/mixins/map/mixin.style.js +4 -67
- package/map/client/mixins/map/mixin.tiled-mesh-layers.js +2 -1
- package/map/client/mixins/map/mixin.tiled-wind-layers.js +4 -2
- package/map/client/mixins/map/mixin.tooltip.js +2 -1
- package/map/client/mixins/mixin.activity.js +71 -192
- package/map/client/mixins/mixin.catalog-panel.js +6 -6
- package/map/client/mixins/mixin.context.js +12 -9
- package/map/client/mixins/mixin.feature-service.js +29 -300
- package/map/client/mixins/mixin.weacast.js +11 -17
- package/map/client/pixi-utils.js +1 -1
- package/map/client/planets.js +66 -0
- package/map/client/utils/index.js +6 -0
- package/map/client/utils/utils.capture.js +176 -0
- package/map/client/utils/utils.catalog.js +166 -0
- package/map/client/utils/utils.features.js +364 -0
- package/map/client/utils/utils.js +0 -151
- package/map/client/utils/utils.layers.js +175 -0
- package/map/client/utils/utils.location.js +91 -23
- package/map/client/utils/utils.project.js +8 -0
- package/map/client/utils/utils.schema.js +0 -1
- package/map/client/utils/utils.style.js +309 -0
- package/map/client/utils.all.js +2 -2
- package/map/client/utils.globe.js +1 -1
- package/map/client/utils.map.js +1 -1
- package/map/common/permissions.js +2 -0
- package/map/common/schemas/capture.create.json +132 -0
- package/map/common/schemas/projects.create.json +52 -0
- package/map/common/schemas/projects.update.json +52 -0
- package/map/common/wms-utils.js +8 -3
- package/package.json +6 -5
- package/test/api/core/account.test.js +20 -0
- package/test/api/core/config/default.cjs +16 -3
- package/test/api/core/import-export.test.js +86 -0
- package/test/api/core/test-log-2024-01-04.log +14 -0
- package/test/api/map/catalog.test.js +164 -0
- package/test/api/map/index.test.js +25 -61
- package/test/api/map/test-log-2024-01-04.log +2 -0
- package/test/api/map/test-log-2024-01-11.log +1 -0
- package/test/api/map/test-log-2024-01-25.log +19 -0
- package/test/client/core/layout.js +24 -5
- package/test/client/core/utils.js +7 -0
- package/test/client/map/catalog.js +78 -1
- package/test/client/map/time.js +2 -1
- package/core/client/components/screen/KEndpointScreen.vue +0 -80
- package/core/client/mixins/mixin.account.js +0 -61
- package/extras/icons/kdk.png +0 -0
- package/map/api/services/geocoder/geocoder.service.js +0 -79
- package/map/client/cesium/utils.js +0 -133
- package/map/client/components/KCaptureToolbar.vue +0 -155
- package/map/client/components/KColorLegend.vue +0 -349
- package/map/client/components/KTimeline.vue +0 -293
- package/map/client/components/KUrlLegend.vue +0 -122
- package/map/client/leaflet/utils.js +0 -246
package/.travis.test.sh
CHANGED
|
@@ -3,23 +3,56 @@
|
|
|
3
3
|
check_code()
|
|
4
4
|
{
|
|
5
5
|
if [ $1 -eq 1 ]; then
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
echo "$2 has failed [error: $1]"
|
|
7
|
+
exit 1
|
|
8
8
|
fi
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
AGE_VERSION=1.1.1
|
|
12
|
+
SOPS_VERSION=3.8.1
|
|
13
|
+
TMP_PATH="$(mktemp -d -p "${XDG_RUNTIME_DIR:-}" kalisio.XXXXXX)"
|
|
14
|
+
|
|
15
|
+
install_age() {
|
|
16
|
+
local DL_PATH="$TMP_PATH/age"
|
|
17
|
+
mkdir "$DL_PATH" && pushd "$DL_PATH" || exit
|
|
18
|
+
wget -q https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-linux-amd64.tar.gz
|
|
19
|
+
# no checksum ...
|
|
20
|
+
tar xf age-v${AGE_VERSION}-linux-amd64.tar.gz
|
|
21
|
+
cp age/age "$HOME/.local/bin"
|
|
22
|
+
popd || exit
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
install_sops() {
|
|
26
|
+
local DL_PATH="$TMP_PATH/sops"
|
|
27
|
+
mkdir "$DL_PATH" && pushd "$DL_PATH" || exit
|
|
28
|
+
wget -q https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64
|
|
29
|
+
wget -q https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.checksums.txt
|
|
30
|
+
sha256sum --ignore-missing --quiet -c sops-v${SOPS_VERSION}.checksums.txt
|
|
31
|
+
cp sops-v${SOPS_VERSION}.linux.amd64 "$HOME/.local/bin/sops"
|
|
32
|
+
chmod a+x "$HOME/.local/bin/sops"
|
|
33
|
+
popd || exit
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Make sure that folder exists, with Travis CI it's already in the PATH
|
|
37
|
+
mkdir -p "$HOME/.local/bin"
|
|
38
|
+
install_age
|
|
39
|
+
install_sops
|
|
40
|
+
|
|
11
41
|
echo -e "machine github.com\n login $GITHUB_TOKEN" > ~/.netrc
|
|
12
42
|
|
|
13
43
|
# Setup KDK
|
|
14
44
|
yarn install
|
|
15
45
|
|
|
16
|
-
# Clone the workspace
|
|
17
|
-
git clone https://github.com/kalisio/kdk-workspaces workspace
|
|
18
|
-
|
|
19
46
|
# Clone others direct dependencies we'd like to use for testing
|
|
20
47
|
git clone https://github.com/kalisio/feathers-distributed && cd feathers-distributed && yarn install && yarn link && cd ..
|
|
21
48
|
yarn link @kalisio/feathers-distributed
|
|
22
49
|
|
|
50
|
+
git clone https://github.com/kalisio/feathers-s3 && cd feathers-s3 && yarn install && yarn link && cd ..
|
|
51
|
+
yarn link @kalisio/feathers-s3
|
|
52
|
+
|
|
53
|
+
git clone https://github.com/kalisio/feathers-import-export && cd feathers-import-export && yarn install && yarn link && yarn link @kalisio/feathers-s3 && cd ..
|
|
54
|
+
yarn link @kalisio/feathers-import-export
|
|
55
|
+
|
|
23
56
|
git clone https://github.com/kalisio/feathers-webpush && cd feathers-webpush && yarn install && yarn link && cd ..
|
|
24
57
|
yarn link @kalisio/feathers-webpush
|
|
25
58
|
|
|
@@ -30,11 +63,10 @@ yarn link @weacast/core
|
|
|
30
63
|
yarn link @weacast/gfs
|
|
31
64
|
yarn link @weacast/probe
|
|
32
65
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
. .env
|
|
37
|
-
set +a
|
|
66
|
+
# Clone the development project and configure the env
|
|
67
|
+
git clone https://oauth2:$GITHUB_TOKEN@github.com/kalisio/development.git "development"
|
|
68
|
+
source development/workspaces/libs/libs.sh kdk
|
|
38
69
|
|
|
70
|
+
# Run the test
|
|
39
71
|
yarn test
|
|
40
72
|
check_code $? "Running tests"
|
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
[](https://kalisio.github.io/kdk/)
|
|
2
2
|
|
|
3
3
|
[](https://github.com/kalisio/kdk/releases)
|
|
4
4
|
[](https://app.travis-ci.com/kalisio/kdk)
|
|
5
5
|
[](https://codeclimate.com/github/kalisio/kdk)
|
|
6
6
|
[](https://codeclimate.com/github/kalisio/kdk/coverage)
|
|
7
|
-
[](https://david-dm.org/kalisio/kdk)
|
|
8
8
|
[](https://kalisio.github.io/kdk/)
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
|
package/core/api/application.js
CHANGED
|
@@ -174,7 +174,7 @@ async function createService (name, app, options = {}) {
|
|
|
174
174
|
paginate,
|
|
175
175
|
multi: true,
|
|
176
176
|
whitelist: [
|
|
177
|
-
'$exists', '$and', '$or', '$eq', '$elemMatch', '$distinct', '$groupBy', '$group', '$regex',
|
|
177
|
+
'$exists', '$and', '$or', '$not', '$eq', '$elemMatch', '$distinct', '$groupBy', '$group', '$regex',
|
|
178
178
|
'$text', '$search', '$caseSensitive', '$language', '$diacriticSensitive',
|
|
179
179
|
'$aggregate', '$near', '$nearSphere', '$geoIntersects', '$geoWithin',
|
|
180
180
|
'$maxDistance', '$minDistance', '$geometry', '$box', '$polygon', '$center', '$centerSphere'
|
|
@@ -182,7 +182,7 @@ async function createService (name, app, options = {}) {
|
|
|
182
182
|
}, options)
|
|
183
183
|
if (serviceOptions.disabled) return undefined
|
|
184
184
|
// For DB services a model has to be provided
|
|
185
|
-
const fileName = serviceOptions.
|
|
185
|
+
const fileName = serviceOptions.modelName || name
|
|
186
186
|
|
|
187
187
|
let dbService = false
|
|
188
188
|
try {
|
|
@@ -523,6 +523,11 @@ export function kdk (config = {}) {
|
|
|
523
523
|
service = await configureService(name, service, servicesPath)
|
|
524
524
|
return service
|
|
525
525
|
}
|
|
526
|
+
// This is used to declare existing service
|
|
527
|
+
app.declareService = function (name, service, serviceOptions) {
|
|
528
|
+
service = declareService(name, app, service, serviceOptions)
|
|
529
|
+
return service
|
|
530
|
+
}
|
|
526
531
|
// This is used to create standard services
|
|
527
532
|
app.createService = async function (name, options) {
|
|
528
533
|
const service = await createService(name, app, options)
|
|
@@ -15,6 +15,22 @@ const { ObjectID } = mongodb
|
|
|
15
15
|
const { oauth, OAuthStrategy } = OAuth
|
|
16
16
|
const { NotAuthenticated } = errors
|
|
17
17
|
|
|
18
|
+
export class Authentication extends AuthenticationService {
|
|
19
|
+
// Feathers does not seem to take input payload into account when renewing the token,
|
|
20
|
+
// see https://github.com/feathersjs/feathers/issues/3419
|
|
21
|
+
async getPayload (authResult, params) {
|
|
22
|
+
// Params can override defaults
|
|
23
|
+
if (params.payload) {
|
|
24
|
+
return params.payload
|
|
25
|
+
} else if (authResult.authentication && authResult.authentication.payload) {
|
|
26
|
+
// Avoid conflicting with default token options added when generating a new token
|
|
27
|
+
return _.omit(authResult.authentication.payload, ['aud', 'iss', 'exp', 'sub', 'iat', 'jti', 'nbf'])
|
|
28
|
+
} else {
|
|
29
|
+
return {}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
export class AuthenticationProviderStrategy extends OAuthStrategy {
|
|
19
35
|
async getEntityData (profile, entity) {
|
|
20
36
|
const createEntity = _.isNil(entity)
|
|
@@ -151,7 +167,7 @@ export default function auth (app) {
|
|
|
151
167
|
if (config.oauth) config.oauth = _.omitBy(config.oauth, _.isNil)
|
|
152
168
|
app.set('authentication', config)
|
|
153
169
|
|
|
154
|
-
const authentication = new
|
|
170
|
+
const authentication = new Authentication(app)
|
|
155
171
|
const strategies = config.authStrategies || []
|
|
156
172
|
if (strategies.includes('jwt')) authentication.register('jwt', new JWTAuthenticationStrategy())
|
|
157
173
|
if (strategies.includes('local')) authentication.register('local', new LocalStrategy())
|
package/core/api/db.js
CHANGED
|
@@ -72,8 +72,13 @@ export function objectifyIDs (object) {
|
|
|
72
72
|
export function toObjectIDs (object, properties) {
|
|
73
73
|
properties.forEach(property => {
|
|
74
74
|
const value = _.get(object, property)
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
const ids = value.map(id => createObjectID(id)).filter(id => id)
|
|
77
|
+
if (ids.length > 0) _.set(object, property, ids)
|
|
78
|
+
} else {
|
|
79
|
+
const id = createObjectID(value)
|
|
80
|
+
if (id) _.set(object, property, id)
|
|
81
|
+
}
|
|
77
82
|
})
|
|
78
83
|
}
|
|
79
84
|
|
|
@@ -14,6 +14,7 @@ export async function verifyGuest (hook) {
|
|
|
14
14
|
}
|
|
15
15
|
const app = hook.app
|
|
16
16
|
const user = hook.result.user
|
|
17
|
+
if (!user) return hook
|
|
17
18
|
debug('verifyGuest hook called on ', user._id)
|
|
18
19
|
|
|
19
20
|
// Check whether the user has been inivted. If not, nothing to do
|
|
@@ -42,6 +43,7 @@ export async function consentGuest (hook) {
|
|
|
42
43
|
}
|
|
43
44
|
const app = hook.app
|
|
44
45
|
const user = hook.result.user
|
|
46
|
+
if (!user) return hook
|
|
45
47
|
debug('consentGuest hook called on ', user._id)
|
|
46
48
|
|
|
47
49
|
// Check whether the user has been invited. If not, nothing to do
|
|
@@ -66,9 +68,9 @@ export async function consentGuest (hook) {
|
|
|
66
68
|
|
|
67
69
|
export function discardAuthenticationProviders (hook) {
|
|
68
70
|
const providers = hook.app.authenticationProviders || []
|
|
69
|
-
|
|
71
|
+
|
|
70
72
|
// Iterate through known providers
|
|
71
73
|
for (const provider of providers) {
|
|
72
74
|
discard(provider)(hook)
|
|
73
75
|
}
|
|
74
|
-
}
|
|
76
|
+
}
|
|
@@ -186,9 +186,19 @@ export async function authorise (hook) {
|
|
|
186
186
|
if (checkAuthorisation) {
|
|
187
187
|
// Build ability for user
|
|
188
188
|
const authorisationService = hook.app.getService('authorisations')
|
|
189
|
-
|
|
189
|
+
// If no user we allow for a stateless token with permissions inside
|
|
190
|
+
let subject = hook.params.user
|
|
191
|
+
if (!subject) {
|
|
192
|
+
const payload = _.get(hook.params, 'authentication.payload')
|
|
193
|
+
// Token targeting API gateway (sub = keyId) or app used through iframe (appId = keyId)
|
|
194
|
+
if (payload && (payload.sub || payload.appId)) {
|
|
195
|
+
subject = Object.assign({ _id: (payload.sub || payload.appId) }, payload)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const abilities = await authorisationService.getAbilities(subject)
|
|
190
199
|
hook.params.abilities = abilities
|
|
191
|
-
debug('User abilities are', abilities.rules)
|
|
200
|
+
if (hook.params.user) debug('User abilities are', abilities.rules)
|
|
201
|
+
else debug('Stateless abilities are', abilities.rules)
|
|
192
202
|
|
|
193
203
|
// Check for access to service fisrt
|
|
194
204
|
if (!hasServiceAbilities(abilities, hook.service)) {
|
|
@@ -8,7 +8,7 @@ import common from 'feathers-hooks-common'
|
|
|
8
8
|
import makeDebug from 'debug'
|
|
9
9
|
|
|
10
10
|
const { Conflict, BadRequest } = errors
|
|
11
|
-
const {
|
|
11
|
+
const { getItems, replaceItems } = common
|
|
12
12
|
const sift = siftModule.default
|
|
13
13
|
const debug = makeDebug('kdk:core:model:hooks')
|
|
14
14
|
|
|
@@ -181,7 +181,7 @@ export async function populatePreviousObject (hook) {
|
|
|
181
181
|
throw new Error('The \'populatePreviousObject\' hook should only be used as a \'before\' hook.')
|
|
182
182
|
}
|
|
183
183
|
const item = getItems(hook)
|
|
184
|
-
const id = (hook.id ? hook.id : item
|
|
184
|
+
const id = (hook.id ? hook.id : _.get(item, '_id'))
|
|
185
185
|
// Retrieve previous version of the item and make it available to next hooks
|
|
186
186
|
if (id) {
|
|
187
187
|
hook.params.previousItem = await hook.service.get(id.toString())
|
|
@@ -264,20 +264,20 @@ export function checkUnique (options = {}) {
|
|
|
264
264
|
// Prevent patch service calls from changing certain fields.
|
|
265
265
|
// Based on https://hooks-common.feathersjs.com/hooks.html#preventchanges
|
|
266
266
|
// but updated to handle dot notation
|
|
267
|
-
export function preventChanges(ifThrow, fieldNames) {
|
|
267
|
+
export function preventChanges (ifThrow, fieldNames) {
|
|
268
268
|
return (hook) => {
|
|
269
269
|
if ((hook.type !== 'before') || (hook.method !== 'patch')) {
|
|
270
270
|
throw new Error('The \'preventChanges\' hook should only be used as a \'before\' patch hook.')
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
const data = { ...hook.data }
|
|
274
274
|
// Check all data fields
|
|
275
275
|
_.forOwn(hook.data, (value, key) => {
|
|
276
276
|
fieldNames.forEach(name => {
|
|
277
277
|
// If a prevented field is found or dot notation with a prevented field is used
|
|
278
278
|
if ((key === name) || key.startsWith(`${name}.`)) {
|
|
279
279
|
if (ifThrow) {
|
|
280
|
-
throw new BadRequest(`Field ${name} may not be patched. (preventChanges)`)
|
|
280
|
+
throw new BadRequest(`Field ${name} may not be patched. (preventChanges)`)
|
|
281
281
|
}
|
|
282
282
|
delete data[key]
|
|
283
283
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
2
|
import makeDebug from 'debug'
|
|
3
|
-
import errors from '@feathersjs/errors'
|
|
4
3
|
const debug = makeDebug('kdk:core:organisations:hooks')
|
|
5
4
|
|
|
6
|
-
const { Forbidden } = errors
|
|
7
|
-
|
|
8
5
|
export async function createOrganisationServices (hook) {
|
|
9
6
|
if (hook.type !== 'after') {
|
|
10
7
|
throw new Error('The \'createOrganisationServices\' hook should only be used as a \'after\' hook.')
|
|
@@ -153,4 +150,3 @@ export function removeOrganisationResources (resourceScope) {
|
|
|
153
150
|
return hook
|
|
154
151
|
}
|
|
155
152
|
}
|
|
156
|
-
|
|
@@ -8,11 +8,15 @@ export default {
|
|
|
8
8
|
all: [],
|
|
9
9
|
find: [],
|
|
10
10
|
get: [],
|
|
11
|
-
create: [
|
|
12
|
-
|
|
11
|
+
create: [
|
|
12
|
+
when(
|
|
13
|
+
(hook) => ['passwordChange', 'resetPwdShort', 'verifySignupSetPasswordLong', 'verifySignupSetPasswordShort'].includes(hook.data.action),
|
|
14
|
+
enforcePasswordPolicy({ userAsItem: false, passwordField: 'value.password' })
|
|
15
|
+
),
|
|
16
|
+
],
|
|
13
17
|
update: [],
|
|
14
18
|
patch: [],
|
|
15
|
-
remove: []
|
|
19
|
+
remove: [],
|
|
16
20
|
},
|
|
17
21
|
|
|
18
22
|
after: {
|
|
@@ -22,7 +26,7 @@ export default {
|
|
|
22
26
|
create: [],
|
|
23
27
|
update: [],
|
|
24
28
|
patch: [],
|
|
25
|
-
remove: []
|
|
29
|
+
remove: [],
|
|
26
30
|
},
|
|
27
31
|
|
|
28
32
|
error: {
|
|
@@ -32,6 +36,6 @@ export default {
|
|
|
32
36
|
create: [],
|
|
33
37
|
update: [],
|
|
34
38
|
patch: [],
|
|
35
|
-
remove: []
|
|
36
|
-
}
|
|
39
|
+
remove: [],
|
|
40
|
+
},
|
|
37
41
|
}
|
|
@@ -103,7 +103,7 @@ export default function (name, app, options) {
|
|
|
103
103
|
|
|
104
104
|
const servicePath = app.get('apiPath') + '/account'
|
|
105
105
|
const userService = app.getService('users')
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
return new AccountService(app, {
|
|
108
108
|
// By default it is impossible to reset password if email is not verified
|
|
109
109
|
// The problem is that if you loose your password before validating your email you are blocked,
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import commonHooks from 'feathers-hooks-common'
|
|
2
|
+
|
|
1
3
|
export default {
|
|
2
4
|
before: {
|
|
3
5
|
all: [],
|
|
4
|
-
find: [],
|
|
5
|
-
get: [],
|
|
6
|
+
find: [commonHooks.disallow()],
|
|
7
|
+
get: [commonHooks.disallow()],
|
|
6
8
|
create: [],
|
|
7
|
-
update: [],
|
|
8
|
-
patch: [],
|
|
9
|
-
remove: []
|
|
9
|
+
update: [commonHooks.disallow()],
|
|
10
|
+
patch: [commonHooks.disallow()],
|
|
11
|
+
remove: [commonHooks.disallow()]
|
|
10
12
|
},
|
|
11
13
|
|
|
12
14
|
after: {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Service } from '@kalisio/feathers-import-export'
|
|
2
|
+
import makeDebug from 'debug'
|
|
3
|
+
|
|
4
|
+
const debug = makeDebug('kdk:import-export:service')
|
|
5
|
+
|
|
6
|
+
export default function (name, app, options) {
|
|
7
|
+
const config = app.get('import-export')
|
|
8
|
+
debug('Creating import-export service with config ', config)
|
|
9
|
+
const s3ServicePath = app.get('apiPath') + '/' + (config.s3Service)
|
|
10
|
+
return new Service(Object.assign({ app }, config, { s3ServicePath }))
|
|
11
|
+
}
|
|
@@ -94,7 +94,10 @@ export default async function () {
|
|
|
94
94
|
if (authConfig) {
|
|
95
95
|
await app.createService('users', { modelsPath, servicesPath })
|
|
96
96
|
debug('\'users\' service created')
|
|
97
|
-
await app.createService('account', {
|
|
97
|
+
await app.createService('account', {
|
|
98
|
+
servicesPath,
|
|
99
|
+
methods: ['create', 'verifyEmail']
|
|
100
|
+
})
|
|
98
101
|
debug('\'account\' service created')
|
|
99
102
|
await app.createService('authorisations', { servicesPath })
|
|
100
103
|
debug('\'authorisations\' service created')
|
|
@@ -106,6 +109,15 @@ export default async function () {
|
|
|
106
109
|
debug('\'storage\' service created')
|
|
107
110
|
}
|
|
108
111
|
|
|
112
|
+
const importExportConfig = app.get('import-export')
|
|
113
|
+
if (importExportConfig) {
|
|
114
|
+
await app.createService('import-export', {
|
|
115
|
+
servicesPath,
|
|
116
|
+
events: ['import-created', 'import-completed', 'export-created', 'export-completed']
|
|
117
|
+
}, app)
|
|
118
|
+
debug('\'import-export\' service created')
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
const orgConfig = app.get('organisations')
|
|
110
122
|
if (orgConfig) {
|
|
111
123
|
await createOrganisationService.call(app)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash'
|
|
2
1
|
import {
|
|
3
2
|
serialize, updateAbilities, populatePreviousObject, hashPassword, disallowRegistration, allowLocalAuthentication,
|
|
4
3
|
discardAuthenticationProviders, enforcePasswordPolicy, storePreviousPassword, sendNewSubscriptionEmail
|
|
@@ -49,11 +48,11 @@ export default {
|
|
|
49
48
|
commonHooks.when(hook => hook.params.provider,
|
|
50
49
|
commonHooks.discard('password'),
|
|
51
50
|
commonHooks.discard('previousPasswords'),
|
|
52
|
-
discardAuthenticationProviders)
|
|
51
|
+
discardAuthenticationProviders)
|
|
53
52
|
// Hide profile for external user as it may contain personal information
|
|
54
53
|
// However, this causes an issue: https://github.com/feathersjs-ecosystem/feathers-reactive/issues/214
|
|
55
54
|
// So let the application decide what to do
|
|
56
|
-
//commonHooks.when(isNotMe, commonHooks.discard('profile'))
|
|
55
|
+
// commonHooks.when(isNotMe, commonHooks.discard('profile'))
|
|
57
56
|
],
|
|
58
57
|
find: [],
|
|
59
58
|
get: [],
|
package/core/client/api.js
CHANGED
|
@@ -7,10 +7,10 @@ import feathers from '@feathersjs/client'
|
|
|
7
7
|
import { io } from 'socket.io-client'
|
|
8
8
|
import reactive from 'feathers-reactive/dist/feathers-reactive.js'
|
|
9
9
|
import configuration from 'config'
|
|
10
|
-
import { Platform } from 'quasar'
|
|
11
10
|
import { permissions } from '../common/index.js'
|
|
12
11
|
import { Store } from './store.js'
|
|
13
12
|
import { Events } from './events.js'
|
|
13
|
+
import { LocalStorage } from './local-storage.js'
|
|
14
14
|
|
|
15
15
|
// Setup log level
|
|
16
16
|
if (_.get(configuration, 'logs.level')) {
|
|
@@ -22,10 +22,7 @@ if (_.get(configuration, 'logs.level')) {
|
|
|
22
22
|
export function createClient (config) {
|
|
23
23
|
// Initiate the client
|
|
24
24
|
const api = feathers()
|
|
25
|
-
|
|
26
|
-
function getBaseUrlStorageKey () {
|
|
27
|
-
return config.appName.toLowerCase() + '-baseUrl'
|
|
28
|
-
}
|
|
25
|
+
const baseUrlStorageKey = 'baseUrl'
|
|
29
26
|
|
|
30
27
|
// Matchers that can be added to customize route guards
|
|
31
28
|
let matchers = []
|
|
@@ -142,7 +139,7 @@ export function createClient (config) {
|
|
|
142
139
|
}
|
|
143
140
|
// Change the base URL/domain to be used (useful for mobile apps)
|
|
144
141
|
api.setBaseUrl = function (baseUrl) {
|
|
145
|
-
|
|
142
|
+
LocalStorage.set(baseUrlStorageKey, baseUrl)
|
|
146
143
|
// Updating this setting live does not seem to work well in Feathers
|
|
147
144
|
// For now the caller should simply "reload" the app
|
|
148
145
|
/*
|
|
@@ -162,15 +159,19 @@ export function createClient (config) {
|
|
|
162
159
|
}
|
|
163
160
|
*/
|
|
164
161
|
}
|
|
165
|
-
|
|
162
|
+
// Get the base URL/domain to be used (useful for mobile apps)
|
|
166
163
|
api.getBaseUrl = function () {
|
|
167
|
-
//
|
|
168
|
-
|
|
164
|
+
// We can override the default app origin anyway
|
|
165
|
+
const origin = config.origin || window.location.origin
|
|
169
166
|
// Check for registered custom base Url if any
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
167
|
+
return LocalStorage.get(baseUrlStorageKey, origin)
|
|
168
|
+
}
|
|
169
|
+
// Helper fonctions to access/alter config used at creation time
|
|
170
|
+
api.getConfig = function (path) {
|
|
171
|
+
return (path ? _.get(config, path) : config)
|
|
172
|
+
}
|
|
173
|
+
api.setConfig = function (path, value) {
|
|
174
|
+
_.set(config, path, value)
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
api.can = function () {
|
|
@@ -287,7 +288,8 @@ export function createClient (config) {
|
|
|
287
288
|
// eg it might be imported before another one updating the config.
|
|
288
289
|
// It is up to the application to instanciate the client when required.
|
|
289
290
|
export let api
|
|
290
|
-
export function initializeApi () {
|
|
291
|
+
export function initializeApi (fn) {
|
|
291
292
|
api = createClient(configuration)
|
|
293
|
+
if (fn) fn.call(api, configuration)
|
|
292
294
|
return api
|
|
293
295
|
}
|
|
@@ -11,12 +11,16 @@ export const Capabilities = {
|
|
|
11
11
|
const content = await capabilities.json()
|
|
12
12
|
logger.debug('[KDK] fetched capabilities:', JSON.stringify(content, null, 4))
|
|
13
13
|
this.content = content
|
|
14
|
+
// Backend might override some defaults in client config
|
|
15
|
+
_.forOwn(_.pick(content, ['gateway']), (value, key) => {
|
|
16
|
+
api.setConfig(key, value)
|
|
17
|
+
})
|
|
14
18
|
// Used to ensure backward compatibility
|
|
15
19
|
Store.set('capabilities.api', content)
|
|
16
20
|
Store.set('capabilities.client', _.pick(config, ['version', 'buildNumber']))
|
|
17
21
|
},
|
|
18
|
-
get (key) {
|
|
22
|
+
get (key, defaultValue) {
|
|
19
23
|
if (!this.content) logger.error(new Error('Capabilities must be initialized first'))
|
|
20
|
-
else return _.get(this.content, key)
|
|
24
|
+
else return _.get(this.content, key, defaultValue)
|
|
21
25
|
}
|
|
22
26
|
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<template v-for="component in avaiableComponents" :key="component.uid">
|
|
4
|
-
<component
|
|
4
|
+
<Suspense v-if="component.suspense">
|
|
5
|
+
<component
|
|
6
|
+
v-if="component.isVisible && !component.isHidden"
|
|
7
|
+
:is="component.instance"
|
|
8
|
+
:context="context"
|
|
9
|
+
:renderer="component.renderer ? component.renderer: actionRenderer"
|
|
10
|
+
v-bind="component"
|
|
11
|
+
v-on="component.on ? { [component.on.event]: component.on.listener } : {}"
|
|
12
|
+
@triggered="onTriggered" />
|
|
13
|
+
</Suspense>
|
|
14
|
+
<component v-else
|
|
5
15
|
v-if="component.isVisible && !component.isHidden"
|
|
6
16
|
:is="component.instance"
|
|
7
17
|
:context="context"
|
|
@@ -7,21 +7,23 @@
|
|
|
7
7
|
:toolbar="toolbar"
|
|
8
8
|
:width-policy="widthPolicy"
|
|
9
9
|
>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
<Suspense>
|
|
11
|
+
<!-- component with v-model -->
|
|
12
|
+
<component
|
|
13
|
+
v-if="_.has(attrs, 'v-model')"
|
|
14
|
+
ref="componentRef"
|
|
15
|
+
:is="computedComponent"
|
|
16
|
+
v-model="computedModel"
|
|
17
|
+
v-bind="computedProps"
|
|
18
|
+
/>
|
|
19
|
+
<!-- component without v-model -->
|
|
20
|
+
<component
|
|
21
|
+
v-else
|
|
22
|
+
ref="componentRef"
|
|
23
|
+
:is="computedComponent"
|
|
24
|
+
v-bind="computedProps"
|
|
25
|
+
/>
|
|
26
|
+
</Suspense>
|
|
25
27
|
</KModal>
|
|
26
28
|
</template>
|
|
27
29
|
|
|
@@ -112,7 +112,7 @@ const xlMinWidths = { wide: 90, medium: 55, narrow: 25 }
|
|
|
112
112
|
const computedStyle = computed(() => {
|
|
113
113
|
// compute the modal max height
|
|
114
114
|
const screenHeight = $q.screen.height
|
|
115
|
-
const modalMaxHeight = props.maximized ? screenHeight : 0.
|
|
115
|
+
const modalMaxHeight = props.maximized ? screenHeight : 0.9 * screenHeight
|
|
116
116
|
// compute the scroll area max height
|
|
117
117
|
// take into account the header and footer height
|
|
118
118
|
let contentMaxHeight = modalMaxHeight - headerHeight.value
|
|
@@ -63,6 +63,10 @@ const props = defineProps({
|
|
|
63
63
|
dense: {
|
|
64
64
|
type: Boolean,
|
|
65
65
|
default: false
|
|
66
|
+
},
|
|
67
|
+
isExpanded: {
|
|
68
|
+
type: Boolean,
|
|
69
|
+
default: false
|
|
66
70
|
}
|
|
67
71
|
})
|
|
68
72
|
|
|
@@ -70,7 +74,7 @@ const props = defineProps({
|
|
|
70
74
|
const scrollArea = ref(null)
|
|
71
75
|
const scrollAreaKey = ref(0)
|
|
72
76
|
const isExpandable = ref(false)
|
|
73
|
-
const isExpanded = ref(
|
|
77
|
+
const isExpanded = ref(props.isExpanded)
|
|
74
78
|
const isScrollable = ref(false)
|
|
75
79
|
|
|
76
80
|
// computed
|
|
@@ -78,6 +78,7 @@ import { ref, computed, onBeforeUnmount } from 'vue'
|
|
|
78
78
|
import { openURL, useQuasar } from 'quasar'
|
|
79
79
|
import { Store, api } from '../..'
|
|
80
80
|
import { loadComponent } from '../../utils'
|
|
81
|
+
import { LocalStorage } from '../../local-storage.js'
|
|
81
82
|
import KAction from '../KAction.vue'
|
|
82
83
|
|
|
83
84
|
// Data
|
|
@@ -94,11 +95,8 @@ const dense = computed(() => {
|
|
|
94
95
|
})
|
|
95
96
|
|
|
96
97
|
// functions
|
|
97
|
-
function getWelcomeKey () {
|
|
98
|
-
return _.get(config, 'appName').toLowerCase() + '-welcome'
|
|
99
|
-
}
|
|
100
98
|
function show () {
|
|
101
|
-
const canShow =
|
|
99
|
+
const canShow = LocalStorage.get('welcome')
|
|
102
100
|
// Introduction is only for logged users
|
|
103
101
|
showWelcome.value = (_.isNil(canShow) ? _.get(config, 'layout.welcome', true) : JSON.parse(canShow))
|
|
104
102
|
}
|
|
@@ -106,7 +104,7 @@ function hide () {
|
|
|
106
104
|
showWelcome.value = false
|
|
107
105
|
}
|
|
108
106
|
function onToggleIntroduction (toggle) {
|
|
109
|
-
|
|
107
|
+
LocalStorage.set('welcome', !toggle)
|
|
110
108
|
}
|
|
111
109
|
function onOnlineHelp () {
|
|
112
110
|
const onlineHelp = _.get(config, 'appOnlineHelp')
|