mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 03:42:37 +02:00
Merge pull request #2024 from umap-project/permissions-to-modules
chore: move permissions to modules
This commit is contained in:
commit
f196bda25f
6 changed files with 291 additions and 282 deletions
|
@ -1,4 +1,4 @@
|
|||
// Uses U.DataLayerPermissions, U.Marker, U.Polygon, U.Polyline, U.TableEditor not yet ES modules
|
||||
// Uses U.Marker, U.Polygon, U.Polyline, U.TableEditor not yet ES modules
|
||||
// Uses U.FormBuilder not available as ESM
|
||||
|
||||
// FIXME: this module should not depend on Leaflet
|
||||
|
@ -18,6 +18,7 @@ import {
|
|||
uMapAlertConflict as AlertConflict,
|
||||
} from '../../components/alerts/alert.js'
|
||||
import { translate } from '../i18n.js'
|
||||
import { DataLayerPermissions } from '../permissions.js'
|
||||
|
||||
export const LAYER_TYPES = [DefaultLayer, Cluster, Heat, Choropleth, Categorized]
|
||||
|
||||
|
@ -67,7 +68,7 @@ export class DataLayer {
|
|||
}
|
||||
this.backupOptions()
|
||||
this.connectToMap()
|
||||
this.permissions = new U.DataLayerPermissions(this)
|
||||
this.permissions = new DataLayerPermissions(this)
|
||||
if (!this.umap_id) {
|
||||
if (this.showAtLoad()) this.show()
|
||||
this.isDirty = true
|
||||
|
|
|
@ -27,6 +27,7 @@ import Tooltip from './ui/tooltip.js'
|
|||
import URLs from './urls.js'
|
||||
import * as Utils from './utils.js'
|
||||
import { DataLayer, LAYER_TYPES } from './data/layer.js'
|
||||
import { DataLayerPermissions, MapPermissions } from './permissions.js'
|
||||
|
||||
// Import modules and export them to the global scope.
|
||||
// For the not yet module-compatible JS out there.
|
||||
|
@ -41,6 +42,7 @@ window.U = {
|
|||
Browser,
|
||||
Caption,
|
||||
DataLayer,
|
||||
DataLayerPermissions,
|
||||
Dialog,
|
||||
EditPanel,
|
||||
Facets,
|
||||
|
@ -50,6 +52,7 @@ window.U = {
|
|||
HTTPError,
|
||||
Importer,
|
||||
LAYER_TYPES,
|
||||
MapPermissions,
|
||||
NOKError,
|
||||
Orderable,
|
||||
Panel,
|
||||
|
|
285
umap/static/umap/js/modules/permissions.js
Normal file
285
umap/static/umap/js/modules/permissions.js
Normal file
|
@ -0,0 +1,285 @@
|
|||
import { DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||
import { translate } from './i18n.js'
|
||||
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
||||
import * as Utils from './utils.js'
|
||||
|
||||
// Dedicated object so we can deal with a separate dirty status, and thus
|
||||
// call the endpoint only when needed, saving one call at each save.
|
||||
export class MapPermissions {
|
||||
constructor(map) {
|
||||
this.setOptions(map.options.permissions)
|
||||
this.map = map
|
||||
this._isDirty = false
|
||||
}
|
||||
|
||||
set isDirty(status) {
|
||||
this._isDirty = status
|
||||
if (status) this.map.isDirty = status
|
||||
}
|
||||
|
||||
get isDirty() {
|
||||
return this._isDirty
|
||||
}
|
||||
|
||||
setOptions(options) {
|
||||
this.options = Object.assign(
|
||||
{
|
||||
owner: null,
|
||||
editors: [],
|
||||
share_status: null,
|
||||
edit_status: null,
|
||||
},
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
isOwner() {
|
||||
return this.map.options.user?.id === this.map.options.permissions.owner?.id
|
||||
}
|
||||
|
||||
isAnonymousMap() {
|
||||
return !this.map.options.permissions.owner
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.map
|
||||
}
|
||||
|
||||
edit() {
|
||||
if (this.map.options.editMode !== 'advanced') return
|
||||
if (!this.map.options.umap_id) {
|
||||
return Alert.info(translate('Please save the map first'))
|
||||
}
|
||||
const container = DomUtil.create('div', 'permissions-panel')
|
||||
const fields = []
|
||||
DomUtil.createTitle(container, translate('Update permissions'), 'icon-key')
|
||||
if (this.isAnonymousMap()) {
|
||||
if (this.options.anonymous_edit_url) {
|
||||
const helpText = `${translate('Secret edit link:')}<br>${
|
||||
this.options.anonymous_edit_url
|
||||
}`
|
||||
DomUtil.element({
|
||||
tagName: 'p',
|
||||
className: 'help-text',
|
||||
innerHTML: helpText,
|
||||
parent: container,
|
||||
})
|
||||
fields.push([
|
||||
'options.edit_status',
|
||||
{
|
||||
handler: 'IntSelect',
|
||||
label: translate('Who can edit'),
|
||||
selectOptions: this.map.options.edit_statuses,
|
||||
helpText: helpText,
|
||||
},
|
||||
])
|
||||
}
|
||||
} else {
|
||||
if (this.isOwner()) {
|
||||
fields.push([
|
||||
'options.edit_status',
|
||||
{
|
||||
handler: 'IntSelect',
|
||||
label: translate('Who can edit'),
|
||||
selectOptions: this.map.options.edit_statuses,
|
||||
},
|
||||
])
|
||||
fields.push([
|
||||
'options.share_status',
|
||||
{
|
||||
handler: 'IntSelect',
|
||||
label: translate('Who can view'),
|
||||
selectOptions: this.map.options.share_statuses,
|
||||
},
|
||||
])
|
||||
fields.push([
|
||||
'options.owner',
|
||||
{ handler: 'ManageOwner', label: translate("Map's owner") },
|
||||
])
|
||||
}
|
||||
fields.push([
|
||||
'options.editors',
|
||||
{ handler: 'ManageEditors', label: translate("Map's editors") },
|
||||
])
|
||||
}
|
||||
|
||||
const builder = new U.FormBuilder(this, fields)
|
||||
const form = builder.build()
|
||||
container.appendChild(form)
|
||||
if (this.isAnonymousMap() && this.map.options.user) {
|
||||
// We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
|
||||
// Note: real check is made on the back office anyway.
|
||||
const advancedActions = DomUtil.createFieldset(
|
||||
container,
|
||||
translate('Advanced actions')
|
||||
)
|
||||
const advancedButtons = DomUtil.create('div', 'button-bar', advancedActions)
|
||||
DomUtil.createButton(
|
||||
'button',
|
||||
advancedButtons,
|
||||
translate('Attach the map to my account'),
|
||||
this.attach,
|
||||
this
|
||||
)
|
||||
}
|
||||
DomUtil.add('h4', '', container, translate('Datalayers'))
|
||||
this.map.eachDataLayer((datalayer) => {
|
||||
datalayer.permissions.edit(container)
|
||||
})
|
||||
this.map.editPanel.open({ content: container, className: 'dark' })
|
||||
}
|
||||
|
||||
async attach() {
|
||||
const [data, response, error] = await this.map.server.post(this.getAttachUrl())
|
||||
if (!error) {
|
||||
this.options.owner = this.map.options.user
|
||||
Alert.success(translate('Map has been attached to your account'))
|
||||
this.map.editPanel.close()
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (!this.isDirty) return this.map.continueSaving()
|
||||
const formData = new FormData()
|
||||
if (!this.isAnonymousMap() && this.options.editors) {
|
||||
const editors = this.options.editors.map((u) => u.id)
|
||||
for (let i = 0; i < this.options.editors.length; i++)
|
||||
formData.append('editors', this.options.editors[i].id)
|
||||
}
|
||||
if (this.isOwner() || this.isAnonymousMap())
|
||||
formData.append('edit_status', this.options.edit_status)
|
||||
if (this.isOwner()) {
|
||||
formData.append('owner', this.options.owner?.id)
|
||||
formData.append('share_status', this.options.share_status)
|
||||
}
|
||||
const [data, response, error] = await this.map.server.post(
|
||||
this.getUrl(),
|
||||
{},
|
||||
formData
|
||||
)
|
||||
if (!error) {
|
||||
this.commit()
|
||||
this.isDirty = false
|
||||
this.map.continueSaving()
|
||||
this.map.fire('postsync')
|
||||
}
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
return Utils.template(this.map.options.urls.map_update_permissions, {
|
||||
map_id: this.map.options.umap_id,
|
||||
})
|
||||
}
|
||||
|
||||
getAttachUrl() {
|
||||
return Utils.template(this.map.options.urls.map_attach_owner, {
|
||||
map_id: this.map.options.umap_id,
|
||||
})
|
||||
}
|
||||
|
||||
addOwnerLink(element, container) {
|
||||
if (this.options.owner?.name && this.options.owner.url) {
|
||||
const ownerContainer = DomUtil.add(
|
||||
element,
|
||||
'umap-map-owner',
|
||||
container,
|
||||
` ${translate('by')} `
|
||||
)
|
||||
DomUtil.createLink(
|
||||
'',
|
||||
ownerContainer,
|
||||
this.options.owner.name,
|
||||
this.options.owner.url
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commit() {
|
||||
this.map.options.permissions = Object.assign(
|
||||
this.map.options.permissions,
|
||||
this.options
|
||||
)
|
||||
}
|
||||
|
||||
getShareStatusDisplay() {
|
||||
return Object.fromEntries(this.map.options.share_statuses)[
|
||||
this.options.share_status
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export class DataLayerPermissions {
|
||||
constructor(datalayer) {
|
||||
this.options = Object.assign(
|
||||
{
|
||||
edit_status: null,
|
||||
},
|
||||
datalayer.options.permissions
|
||||
)
|
||||
|
||||
this.datalayer = datalayer
|
||||
this._isDirty = false
|
||||
}
|
||||
|
||||
set isDirty(status) {
|
||||
this._isDirty = status
|
||||
if (status) this.datalayer.isDirty = status
|
||||
}
|
||||
|
||||
get isDirty() {
|
||||
return this._isDirty
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this.datalayer.map
|
||||
}
|
||||
|
||||
edit(container) {
|
||||
const fields = [
|
||||
[
|
||||
'options.edit_status',
|
||||
{
|
||||
handler: 'IntSelect',
|
||||
label: translate('Who can edit "{layer}"', {
|
||||
layer: this.datalayer.getName(),
|
||||
}),
|
||||
selectOptions: this.datalayer.map.options.datalayer_edit_statuses,
|
||||
},
|
||||
],
|
||||
]
|
||||
const builder = new U.FormBuilder(this, fields, {
|
||||
className: 'umap-form datalayer-permissions',
|
||||
})
|
||||
const form = builder.build()
|
||||
container.appendChild(form)
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
return Utils.template(this.datalayer.map.options.urls.datalayer_permissions, {
|
||||
map_id: this.datalayer.map.options.umap_id,
|
||||
pk: this.datalayer.umap_id,
|
||||
})
|
||||
}
|
||||
async save() {
|
||||
if (!this.isDirty) return this.datalayer.map.continueSaving()
|
||||
const formData = new FormData()
|
||||
formData.append('edit_status', this.options.edit_status)
|
||||
const [data, response, error] = await this.datalayer.map.server.post(
|
||||
this.getUrl(),
|
||||
{},
|
||||
formData
|
||||
)
|
||||
if (!error) {
|
||||
this.commit()
|
||||
this.isDirty = false
|
||||
this.datalayer.map.continueSaving()
|
||||
}
|
||||
}
|
||||
|
||||
commit() {
|
||||
this.datalayer.options.permissions = Object.assign(
|
||||
this.datalayer.options.permissions,
|
||||
this.options
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
U.DataLayerPermissions = L.Class.extend({
|
||||
options: {
|
||||
edit_status: null,
|
||||
},
|
||||
|
||||
initialize: function (datalayer) {
|
||||
this.options = L.Util.setOptions(this, datalayer.options.permissions)
|
||||
this.datalayer = datalayer
|
||||
let isDirty = false
|
||||
try {
|
||||
Object.defineProperty(this, 'isDirty', {
|
||||
get: () => isDirty,
|
||||
set: (status) => {
|
||||
isDirty = status
|
||||
if (status) this.datalayer.isDirty = status
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
// Certainly IE8, which has a limited version of defineProperty
|
||||
}
|
||||
},
|
||||
|
||||
getMap: function () {
|
||||
return this.datalayer.map
|
||||
},
|
||||
|
||||
edit: function (container) {
|
||||
const fields = [
|
||||
[
|
||||
'options.edit_status',
|
||||
{
|
||||
handler: 'IntSelect',
|
||||
label: L._('Who can edit "{layer}"', { layer: this.datalayer.getName() }),
|
||||
selectOptions: this.datalayer.map.options.datalayer_edit_statuses,
|
||||
},
|
||||
],
|
||||
]
|
||||
const builder = new U.FormBuilder(this, fields, {
|
||||
className: 'umap-form datalayer-permissions',
|
||||
})
|
||||
const form = builder.build()
|
||||
container.appendChild(form)
|
||||
},
|
||||
|
||||
getUrl: function () {
|
||||
return U.Utils.template(this.datalayer.map.options.urls.datalayer_permissions, {
|
||||
map_id: this.datalayer.map.options.umap_id,
|
||||
pk: this.datalayer.umap_id,
|
||||
})
|
||||
},
|
||||
save: async function () {
|
||||
if (!this.isDirty) return this.datalayer.map.continueSaving()
|
||||
const formData = new FormData()
|
||||
formData.append('edit_status', this.options.edit_status)
|
||||
const [data, response, error] = await this.datalayer.map.server.post(
|
||||
this.getUrl(),
|
||||
{},
|
||||
formData
|
||||
)
|
||||
if (!error) {
|
||||
this.commit()
|
||||
this.isDirty = false
|
||||
this.datalayer.map.continueSaving()
|
||||
}
|
||||
},
|
||||
|
||||
commit: function () {
|
||||
L.Util.extend(this.datalayer.options.permissions, this.options)
|
||||
},
|
||||
})
|
|
@ -1,208 +0,0 @@
|
|||
// Dedicated object so we can deal with a separate dirty status, and thus
|
||||
// call the endpoint only when needed, saving one call at each save.
|
||||
U.MapPermissions = L.Class.extend({
|
||||
options: {
|
||||
owner: null,
|
||||
editors: [],
|
||||
share_status: null,
|
||||
edit_status: null,
|
||||
},
|
||||
|
||||
initialize: function (map) {
|
||||
this.setOptions(map.options.permissions)
|
||||
this.map = map
|
||||
let isDirty = false
|
||||
try {
|
||||
Object.defineProperty(this, 'isDirty', {
|
||||
get: () => isDirty,
|
||||
set: (status) => {
|
||||
isDirty = status
|
||||
if (status) {
|
||||
this.map.isDirty = status
|
||||
}
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
// Certainly IE8, which has a limited version of defineProperty
|
||||
}
|
||||
},
|
||||
|
||||
setOptions: function (options) {
|
||||
this.options = L.Util.setOptions(this, options)
|
||||
},
|
||||
|
||||
isOwner: function () {
|
||||
return (
|
||||
this.map.options.user &&
|
||||
this.map.options.permissions.owner &&
|
||||
this.map.options.user.id === this.map.options.permissions.owner.id
|
||||
)
|
||||
},
|
||||
|
||||
isAnonymousMap: function () {
|
||||
return !this.map.options.permissions.owner
|
||||
},
|
||||
|
||||
getMap: function () {
|
||||
return this.map
|
||||
},
|
||||
|
||||
edit: function () {
|
||||
if (this.map.options.editMode !== 'advanced') return
|
||||
if (!this.map.options.umap_id) {
|
||||
return U.Alert.info(L._('Please save the map first'))
|
||||
}
|
||||
const container = L.DomUtil.create('div', 'permissions-panel')
|
||||
const fields = []
|
||||
L.DomUtil.createTitle(container, L._('Update permissions'), 'icon-key')
|
||||
if (this.isAnonymousMap()) {
|
||||
if (this.options.anonymous_edit_url) {
|
||||
const helpText = `${L._('Secret edit link:')}<br>${
|
||||
this.options.anonymous_edit_url
|
||||
}`
|
||||
L.DomUtil.element({
|
||||
tagName: 'p',
|
||||
className: 'help-text',
|
||||
innerHTML: helpText,
|
||||
parent: container,
|
||||
})
|
||||
fields.push([
|
||||
'options.edit_status',
|
||||
{
|
||||
handler: 'IntSelect',
|
||||
label: L._('Who can edit'),
|
||||
selectOptions: this.map.options.edit_statuses,
|
||||
helpText: helpText,
|
||||
},
|
||||
])
|
||||
}
|
||||
} else {
|
||||
if (this.isOwner()) {
|
||||
fields.push([
|
||||
'options.edit_status',
|
||||
{
|
||||
handler: 'IntSelect',
|
||||
label: L._('Who can edit'),
|
||||
selectOptions: this.map.options.edit_statuses,
|
||||
},
|
||||
])
|
||||
fields.push([
|
||||
'options.share_status',
|
||||
{
|
||||
handler: 'IntSelect',
|
||||
label: L._('Who can view'),
|
||||
selectOptions: this.map.options.share_statuses,
|
||||
},
|
||||
])
|
||||
fields.push([
|
||||
'options.owner',
|
||||
{ handler: 'ManageOwner', label: L._("Map's owner") },
|
||||
])
|
||||
}
|
||||
fields.push([
|
||||
'options.editors',
|
||||
{ handler: 'ManageEditors', label: L._("Map's editors") },
|
||||
])
|
||||
}
|
||||
|
||||
const builder = new U.FormBuilder(this, fields)
|
||||
const form = builder.build()
|
||||
container.appendChild(form)
|
||||
if (this.isAnonymousMap() && this.map.options.user) {
|
||||
// We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
|
||||
// Note: real check is made on the back office anyway.
|
||||
const advancedActions = L.DomUtil.createFieldset(
|
||||
container,
|
||||
L._('Advanced actions')
|
||||
)
|
||||
const advancedButtons = L.DomUtil.create('div', 'button-bar', advancedActions)
|
||||
L.DomUtil.createButton(
|
||||
'button',
|
||||
advancedButtons,
|
||||
L._('Attach the map to my account'),
|
||||
this.attach,
|
||||
this
|
||||
)
|
||||
}
|
||||
L.DomUtil.add('h4', '', container, L._('Datalayers'))
|
||||
this.map.eachDataLayer((datalayer) => {
|
||||
datalayer.permissions.edit(container)
|
||||
})
|
||||
this.map.editPanel.open({ content: container, className: 'dark' })
|
||||
},
|
||||
|
||||
attach: async function () {
|
||||
const [data, response, error] = await this.map.server.post(this.getAttachUrl())
|
||||
if (!error) {
|
||||
this.options.owner = this.map.options.user
|
||||
U.Alert.success(L._('Map has been attached to your account'))
|
||||
this.map.editPanel.close()
|
||||
}
|
||||
},
|
||||
|
||||
save: async function () {
|
||||
if (!this.isDirty) return this.map.continueSaving()
|
||||
const formData = new FormData()
|
||||
if (!this.isAnonymousMap() && this.options.editors) {
|
||||
const editors = this.options.editors.map((u) => u.id)
|
||||
for (let i = 0; i < this.options.editors.length; i++)
|
||||
formData.append('editors', this.options.editors[i].id)
|
||||
}
|
||||
if (this.isOwner() || this.isAnonymousMap())
|
||||
formData.append('edit_status', this.options.edit_status)
|
||||
if (this.isOwner()) {
|
||||
formData.append('owner', this.options.owner?.id)
|
||||
formData.append('share_status', this.options.share_status)
|
||||
}
|
||||
const [data, response, error] = await this.map.server.post(
|
||||
this.getUrl(),
|
||||
{},
|
||||
formData
|
||||
)
|
||||
if (!error) {
|
||||
this.commit()
|
||||
this.isDirty = false
|
||||
this.map.continueSaving()
|
||||
this.map.fire('postsync')
|
||||
}
|
||||
},
|
||||
|
||||
getUrl: function () {
|
||||
return U.Utils.template(this.map.options.urls.map_update_permissions, {
|
||||
map_id: this.map.options.umap_id,
|
||||
})
|
||||
},
|
||||
|
||||
getAttachUrl: function () {
|
||||
return U.Utils.template(this.map.options.urls.map_attach_owner, {
|
||||
map_id: this.map.options.umap_id,
|
||||
})
|
||||
},
|
||||
|
||||
addOwnerLink: function (element, container) {
|
||||
if (this.options.owner?.name && this.options.owner.url) {
|
||||
const ownerContainer = L.DomUtil.add(
|
||||
element,
|
||||
'umap-map-owner',
|
||||
container,
|
||||
` ${L._('by')} `
|
||||
)
|
||||
L.DomUtil.createLink(
|
||||
'',
|
||||
ownerContainer,
|
||||
this.options.owner.name,
|
||||
this.options.owner.url
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
commit: function () {
|
||||
L.Util.extend(this.map.options.permissions, this.options)
|
||||
},
|
||||
|
||||
getShareStatusDisplay: function () {
|
||||
return Object.fromEntries(this.map.options.share_statuses)[
|
||||
this.options.share_status
|
||||
]
|
||||
},
|
||||
})
|
|
@ -47,8 +47,6 @@
|
|||
<script src="{% static 'umap/js/umap.forms.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.icon.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.features.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.permissions.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.datalayer.permissions.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/components/fragment.js' %}" defer></script>
|
||||
|
|
Loading…
Reference in a new issue