diff --git a/umap/static/umap/js/modules/data/features.js b/umap/static/umap/js/modules/data/features.js
index f8fe7f5c..f7202b89 100644
--- a/umap/static/umap/js/modules/data/features.js
+++ b/umap/static/umap/js/modules/data/features.js
@@ -108,13 +108,11 @@ class Feature {
const oldGeometry = Utils.CopyJSON(this._geometry)
this.fromLatLngs(this._getLatLngs())
if (sync) {
- console.log('sync geometry')
this.sync.update('geometry', this.geometry, oldGeometry)
}
}
fromLatLngs(latlngs) {
- console.log('fromLatLngs', latlngs)
this._geometry_bk = Utils.CopyJSON(this._geometry)
this._geometry = this.convertLatLngs(latlngs)
}
diff --git a/umap/static/umap/js/modules/data/layer.js b/umap/static/umap/js/modules/data/layer.js
index 459e650a..eaf9a4ce 100644
--- a/umap/static/umap/js/modules/data/layer.js
+++ b/umap/static/umap/js/modules/data/layer.js
@@ -49,7 +49,6 @@ export class DataLayer extends ServerStored {
this._leafletMap = leafletMap
this.parentPane = this._leafletMap.getPane('overlayPane')
this.pane = this._leafletMap.createPane(`datalayer${stamp(this)}`, this.parentPane)
- this.pane.dataset.id = stamp(this)
// FIXME: should be on layer
this.renderer = L.svg({ pane: this.pane })
this.defaultOptions = {
@@ -66,6 +65,7 @@ export class DataLayer extends ServerStored {
data.id = data.id || crypto.randomUUID()
this.setOptions(data)
+ this.pane.dataset.id = this.id
if (!Utils.isObject(this.options.remoteData)) {
this.options.remoteData = {}
@@ -366,9 +366,8 @@ export class DataLayer extends ServerStored {
}
connectToMap() {
- const id = stamp(this)
- if (!this._umap.datalayers[id]) {
- this._umap.datalayers[id] = this
+ if (!this._umap.datalayers[this.id]) {
+ this._umap.datalayers[this.id] = this
}
if (!this._umap.datalayersIndex.includes(this)) {
this._umap.datalayersIndex.push(this)
@@ -419,7 +418,6 @@ export class DataLayer extends ServerStored {
const id = stamp(feature)
if (sync !== false) {
const oldValue = feature.toGeoJSON()
- console.log('oldValue in removeFeature', oldValue)
feature.sync.delete(oldValue)
}
this.hideFeature(feature)
@@ -460,13 +458,13 @@ export class DataLayer extends ServerStored {
.sort(Utils.naturalSort)
}
- addData(geojson, sync) {
+ addData(geojson, sync, batch = true) {
try {
// Do not fail if remote data is somehow invalid,
// otherwise the layer becomes uneditable.
- this.sync.startBatch()
+ if (batch) this.sync.startBatch()
const features = this.makeFeatures(geojson, sync)
- this.sync.commitBatch()
+ if (batch) this.sync.commitBatch()
return features
} catch (err) {
console.debug('Error with DataLayer', this.id)
@@ -1181,7 +1179,7 @@ export class DataLayer extends ServerStored {
}
commitDelete() {
- delete this._umap.datalayers[stamp(this)]
+ delete this._umap.datalayers[this.id]
}
getName() {
diff --git a/umap/static/umap/js/modules/form/fields.js b/umap/static/umap/js/modules/form/fields.js
index 130b0d85..d958c345 100644
--- a/umap/static/umap/js/modules/form/fields.js
+++ b/umap/static/umap/js/modules/form/fields.js
@@ -541,14 +541,14 @@ Fields.DataLayerSwitcher = class extends Fields.Select {
!datalayer.isDataReadOnly() &&
datalayer.isBrowsable()
) {
- options.push([L.stamp(datalayer), datalayer.getName()])
+ options.push([datalayer.id, datalayer.getName()])
}
})
return options
}
toHTML() {
- return L.stamp(this.obj.datalayer)
+ return this.obj.datalayer.id
}
toJS() {
diff --git a/umap/static/umap/js/modules/importer.js b/umap/static/umap/js/modules/importer.js
index 83e09812..f2738f08 100644
--- a/umap/static/umap/js/modules/importer.js
+++ b/umap/static/umap/js/modules/importer.js
@@ -249,7 +249,7 @@ export default class Importer extends Utils.WithTemplate {
tagName: 'option',
parent: layerSelect,
textContent: datalayer.options.name,
- value: L.stamp(datalayer),
+ value: datalayer.id,
})
}
})
diff --git a/umap/static/umap/js/modules/permissions.js b/umap/static/umap/js/modules/permissions.js
index d206496a..9e6f3521 100644
--- a/umap/static/umap/js/modules/permissions.js
+++ b/umap/static/umap/js/modules/permissions.js
@@ -13,6 +13,7 @@ export class MapPermissions extends ServerStored {
this.setProperties(umap.properties.permissions)
this._umap = umap
this._isDirty = false
+ this.sync = umap.syncEngine.proxy(this)
}
setProperties(properties) {
@@ -28,6 +29,13 @@ export class MapPermissions extends ServerStored {
)
}
+ getSyncMetadata() {
+ return {
+ subject: 'mappermissions',
+ metadata: {},
+ }
+ }
+
render() {
this._umap.render(['properties.permissions'])
}
@@ -259,6 +267,14 @@ export class DataLayerPermissions extends ServerStored {
)
this.datalayer = datalayer
+ this.sync = umap.syncEngine.proxy(this)
+ }
+
+ getSyncMetadata() {
+ return {
+ subject: 'datalayerpermissions',
+ metadata: { id: this.datalayer.id },
+ }
}
edit(container) {
diff --git a/umap/static/umap/js/modules/sync/engine.js b/umap/static/umap/js/modules/sync/engine.js
index 90da9258..04a07927 100644
--- a/umap/static/umap/js/modules/sync/engine.js
+++ b/umap/static/umap/js/modules/sync/engine.js
@@ -1,6 +1,12 @@
import * as Utils from '../utils.js'
import { HybridLogicalClock } from './hlc.js'
-import { DataLayerUpdater, FeatureUpdater, MapUpdater } from './updaters.js'
+import {
+ DataLayerUpdater,
+ FeatureUpdater,
+ MapUpdater,
+ MapPermissionsUpdater,
+ DataLayerPermissionsUpdater,
+} from './updaters.js'
import { WebSocketTransport } from './websocket.js'
import { UndoManager } from './undo.js'
import * as SaveManager from '../saving.js'
@@ -56,6 +62,8 @@ export class SyncEngine {
map: new MapUpdater(umap),
feature: new FeatureUpdater(umap),
datalayer: new DataLayerUpdater(umap),
+ mappermissions: new MapPermissionsUpdater(umap),
+ datalayerpermissions: new DataLayerPermissionsUpdater(umap),
}
this.transport = undefined
this._operations = new Operations()
@@ -129,17 +137,15 @@ export class SyncEngine {
this._batch = []
}
- commitBatch() {
+ commitBatch(subject, metadata) {
if (!this._batch.length) {
this._batch = null
return
}
- this._undoManager.add({
- verb: 'batch',
- operations: this._batch,
- })
const operations = this._batch.map((stage) => stage.operation)
- this._send({ verb: 'batch', operations: operations, subject: 'batch' })
+ const operation = { verb: 'batch', operations, subject, metadata }
+ this._undoManager.add({ operation, stages: this._batch })
+ this._send(operation)
this._batch = null
}
@@ -204,6 +210,10 @@ export class SyncEngine {
async save() {
const needSave = new Map()
+ if (!this._umap.id) {
+ // There is no operation for fist map save
+ needSave.set(this._umap, [])
+ }
for (const operation of this._operations.sorted()) {
if (operation.dirty) {
const updater = this._getUpdater(operation.subject)
@@ -215,7 +225,8 @@ export class SyncEngine {
}
}
for (const [obj, operations] of needSave.entries()) {
- await obj.save()
+ const ok = await obj.save()
+ if (!ok) break
for (const operation of operations) {
operation.dirty = false
}
@@ -243,7 +254,11 @@ export class SyncEngine {
}
}
- _getUpdater(subject, metadata) {
+ _getUpdater(subject, metadata, sync) {
+ // For now, prevent permissions to be synced, for security reasons
+ if (sync && (subject === 'mappermissions' || subject === 'datalayerpermissions')) {
+ return
+ }
if (Object.keys(this.updaters).includes(subject)) {
return this.updaters[subject]
}
@@ -256,6 +271,10 @@ export class SyncEngine {
return
}
const updater = this._getUpdater(operation.subject, operation.metadata)
+ if (!updater) {
+ debug('No updater for', operation)
+ return
+ }
updater.applyMessage(operation)
}
@@ -449,7 +468,7 @@ export class SyncEngine {
const handler = {
get(target, prop) {
// Only proxy these methods
- if (['upsert', 'update', 'delete'].includes(prop)) {
+ if (['upsert', 'update', 'delete', 'commitBatch'].includes(prop)) {
const { subject, metadata } = object.getSyncMetadata()
// Reflect.get is calling the original method.
// .bind is adding the parameters automatically
diff --git a/umap/static/umap/js/modules/sync/undo.js b/umap/static/umap/js/modules/sync/undo.js
index a9e7af2e..8ee57890 100644
--- a/umap/static/umap/js/modules/sync/undo.js
+++ b/umap/static/umap/js/modules/sync/undo.js
@@ -19,6 +19,9 @@ export class UndoManager {
for (const button of document.querySelectorAll('.disabled-on-dirty')) {
button.disabled = dirty
}
+ for (const button of document.querySelectorAll('.enabled-on-dirty')) {
+ button.disabled = !dirty
+ }
}
isDirty() {
@@ -33,7 +36,7 @@ export class UndoManager {
add(stage) {
// FIXME make it more generic
- if (stage.operation.key !== '_referenceVersion') {
+ if (!stage.operation || stage.operation.key !== '_referenceVersion') {
stage.operation.dirty = true
this._redoStack = []
this._undoStack.push(stage)
@@ -54,8 +57,8 @@ export class UndoManager {
if (!stage) return
stage.operation.dirty = !stage.operation.dirty
if (stage.operation.verb === 'batch') {
- for (const op of stage.operations) {
- this.applyOperation(this.copyOperation(op, redo))
+ for (const st of stage.stages) {
+ this.applyOperation(this.copyOperation(st, redo))
}
} else {
this.applyOperation(this.copyOperation(stage, redo))
diff --git a/umap/static/umap/js/modules/sync/updaters.js b/umap/static/umap/js/modules/sync/updaters.js
index 082901bf..606ea63f 100644
--- a/umap/static/umap/js/modules/sync/updaters.js
+++ b/umap/static/umap/js/modules/sync/updaters.js
@@ -43,7 +43,6 @@ class BaseUpdater {
export class MapUpdater extends BaseUpdater {
update({ key, value }) {
- console.log('updating', key, value)
if (fieldInSchema(key)) {
this.updateObjectValue(this._umap, key, value)
}
@@ -61,16 +60,11 @@ export class DataLayerUpdater extends BaseUpdater {
upsert({ value }) {
// Upsert only happens when a new datalayer is created.
try {
- console.log(
- 'found datalayer with id',
- value.id,
- this.getDataLayerFromID(value.id)
- )
+ this.getDataLayerFromID(value.id)
} catch {
- console.log('we are the fucking catch', value)
const datalayer = this._umap.createDataLayer(value._umap_options || value, false)
if (value.features) {
- datalayer.addData(value)
+ datalayer.addData(value, true, false)
}
}
}
@@ -149,3 +143,27 @@ export class FeatureUpdater extends BaseUpdater {
return this.getDataLayerFromID(metadata.layerId)
}
}
+
+export class MapPermissionsUpdater extends BaseUpdater {
+ update({ key, value }) {
+ this.updateObjectValue(this._umap.permissions, key, value)
+ // if (fieldInSchema(key)) {
+ // }
+ }
+
+ getStoredObject(metadata) {
+ return this._umap.permissions
+ }
+}
+
+export class DataLayerPermissionsUpdater extends BaseUpdater {
+ update({ key, value, metadata }) {
+ this.updateObjectValue(this.getDataLayerFromID(metadata.id), key, value)
+ // if (fieldInSchema(key)) {
+ // }
+ }
+
+ getStoredObject(metadata) {
+ return this.getDataLayerFromID(metadata.id).permissions
+ }
+}
diff --git a/umap/static/umap/js/modules/ui/bar.js b/umap/static/umap/js/modules/ui/bar.js
index 990ded67..61d079e9 100644
--- a/umap/static/umap/js/modules/ui/bar.js
+++ b/umap/static/umap/js/modules/ui/bar.js
@@ -34,7 +34,7 @@ const TOP_BAR_TEMPLATE = `
${translate('View')}
-