Merge pull request #2121 from umap-project/permissions-revamp

Chore: rework permissions panel
This commit is contained in:
David Larlet 2024-09-13 12:32:49 -04:00 committed by GitHub
commit 52e0259a64
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 133 additions and 93 deletions

View file

@ -307,9 +307,6 @@ input + .help-text {
.formbox.with-switch {
padding-top: 2px;
}
.formbox select {
width: calc(100% - 14px);
}
fieldset.formbox {
border: none;
border-top: 1px solid var(--color-lightGray);
@ -386,6 +383,10 @@ fieldset legend {
font-size: .9rem;
padding: 0 5px;
}
fieldset.separator {
border: none;
border-top: 1px solid var(--color-lightGray);
}
[data-badge] {
position: relative;
@ -633,7 +634,6 @@ i.info {
.umap-datalayer-container,
.umap-layer-properties-container,
.umap-browse-data,
.umap-facet-search,
.umap-tilelayer-switcher-container {
padding: 0 10px;
}

View file

@ -189,5 +189,6 @@
<path id="path8" transform="translate(0 812.36)" d="m8.1903 37.424c-0.48722-0.17411-0.79231-0.44207-0.99467-0.87363l-0.1908-0.40689-1.3703-0.0034c-1.4983-0.0037-1.5911-0.03214-1.5406-0.47225l0.026557-0.23161 2.8423-0.0451 0.19827-0.3895c0.34789-0.68343 0.86349-1.0091 1.5923-1.0058 0.71771 0.0033 1.3415 0.43242 1.6291 1.1207l0.11636 0.27848h4.6574c4.5203 0 4.66 0.0047 4.7428 0.1594 0.1159 0.21656 0.10767 0.30093-0.04419 0.45278-0.11466 0.11466-0.66063 0.1295-4.7632 0.1295h-4.6337l-0.10498 0.2903c-0.15257 0.42194-0.62749 0.84447-1.1103 0.98785-0.49622 0.14736-0.6615 0.14879-1.0524 0.0091zm0.96896-0.84513c0.698-0.33122 0.698-1.3772 0-1.7084-0.35156-0.16682-0.49258-0.16849-0.82704-0.0098-0.33761 0.16021-0.5445 0.51333-0.5445 0.92936 0 0.36801 0.16007 0.60614 0.53566 0.79693 0.31576 0.16039 0.48419 0.15875 0.83587-0.0081z" fill="#f2f2f2" stroke="#999" stroke-width=".25" style="paint-order:fill markers stroke"/>
<path id="path9" transform="translate(0 812.36)" d="m12.773 42.244c-0.4045-0.18728-0.73246-0.52685-0.9029-0.93487l-0.12875-0.30821-7.6214-0.04285-0.026557-0.23161c-0.055311-0.48238-0.16774-0.4688 3.8893-0.46961l3.7189-7.33e-4 0.21287-0.43192c0.65163-1.3222 2.4726-1.2846 3.1257 0.06446l0.17859 0.36892h2.2972c2.2584 0 2.2988 3e-3 2.391 0.17524 0.06808 0.12721 0.06936 0.22886 0.0047 0.37084l-0.08912 0.1956h-4.6108l-0.15493 0.35024c-0.38713 0.87516-1.4373 1.2865-2.2839 0.89448zm1.0632-0.70036c0.37802-0.15795 0.5443-0.42579 0.54116-0.87174-4e-3 -0.57358-0.36338-0.90877-0.9743-0.90877-0.31204 0-0.40386 0.04292-0.64992 0.30383-0.25777 0.27331-0.28248 0.33986-0.2461 0.66262 0.04524 0.40136 0.22819 0.65122 0.5956 0.81339 0.31718 0.14001 0.39991 0.14008 0.73356 6.68e-4z" fill="#f2f2f2" stroke="#999" stroke-width=".25" style="paint-order:fill markers stroke"/>
<path id="path1-67-5" d="m154.68 963.54 4.7344 4.7344-5.0508 4.6836-1.3594-1.4668 3.5274-3.2695-3.2656-3.2656z" color="#000000" fill="#f2f2f2" fill-rule="evenodd" stroke="#999" stroke-width=".25"/>
<path id="copy" d="m58 914.36v3.5h4v4h3.5v-7.5zm-4 4v7.5h7.5v-7.5z" fill="#f2f2f2" style="paint-order:fill markers stroke"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View file

@ -211,5 +211,6 @@
<path style="fill:#f2f2f2;stroke:#999999;stroke-width:0.25;paint-order:fill markers stroke" d="m 8.1902877,37.423896 c -0.4872187,-0.174113 -0.7923051,-0.442073 -0.9946703,-0.873627 l -0.1907982,-0.406887 -1.3702708,-0.0034 c -1.4982701,-0.0037 -1.5910875,-0.03214 -1.5406237,-0.47225 l 0.026557,-0.23161 1.4211632,-0.02255 1.4211632,-0.02255 0.1982698,-0.389502 c 0.347892,-0.683434 0.8634941,-1.009111 1.592299,-1.005764 0.7177082,0.0033 1.3415151,0.432422 1.6290831,1.120669 l 0.116359,0.278485 h 4.65745 c 4.520341,0 4.659962,0.0047 4.742759,0.159402 0.1159,0.216562 0.107667,0.300926 -0.04419,0.452784 -0.114657,0.114657 -0.660626,0.129501 -4.763204,0.129501 h -4.633703 l -0.104975,0.290304 c -0.152574,0.421935 -0.6274874,0.844474 -1.1103021,0.987854 -0.4962156,0.147361 -0.6614998,0.148792 -1.0523644,0.0091 z m 0.9689602,-0.845129 c 0.6980003,-0.331223 0.6980003,-1.377155 0,-1.708378 -0.3515556,-0.166824 -0.4925823,-0.168492 -0.8270409,-0.0098 -0.3376099,0.160207 -0.5444962,0.513326 -0.5444962,0.929363 0,0.368008 0.1600657,0.606145 0.5356626,0.796928 0.3157622,0.16039 0.4841923,0.158752 0.8358745,-0.0081 z" id="path8" transform="translate(0,812.36218)" />
<path style="fill:#f2f2f2;stroke:#999999;stroke-width:0.25;paint-order:fill markers stroke" d="m 12.773494,42.243521 c -0.4045,-0.187277 -0.732456,-0.526846 -0.902897,-0.934868 l -0.128748,-0.308213 -3.8106837,-0.02143 -3.8106834,-0.02142 -0.026557,-0.23161 c -0.055311,-0.482381 -0.1677415,-0.468805 3.8892984,-0.469606 l 3.7189457,-7.33e-4 0.21287,-0.431917 c 0.651632,-1.322166 2.472575,-1.284611 3.125651,0.06446 l 0.178591,0.36892 h 2.297219 c 2.258426,0 2.298803,0.003 2.391007,0.175244 0.06808,0.127207 0.06936,0.228859 0.0047,0.370843 l -0.08912,0.1956 h -2.305381 -2.30538 l -0.15493,0.350241 c -0.387132,0.875164 -1.437261,1.286451 -2.283867,0.894485 z m 1.063202,-0.700358 c 0.378023,-0.157949 0.544298,-0.425794 0.541165,-0.871741 -0.004,-0.573585 -0.363385,-0.908772 -0.974299,-0.908772 -0.312041,0 -0.40386,0.04292 -0.649922,0.303826 -0.257768,0.273313 -0.282484,0.339859 -0.246104,0.662621 0.04524,0.401361 0.228194,0.651219 0.595596,0.813394 0.317179,0.140006 0.399909,0.140082 0.733564,6.68e-4 z" id="path9" transform="translate(0,812.36218)" />
<path style="color:#000000;fill:#f2f2f2;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:0.25;stroke-dasharray:none;stroke-opacity:1" d="m 154.67579,963.53902 4.73437,4.73437 -5.05078,4.6836 -1.35938,-1.4668 3.52735,-3.26953 -3.26563,-3.26563 z" id="path1-67-5" />
<path id="copy" style="fill:#f2f2f2;paint-order:fill markers stroke;fill-opacity:1" d="m 58,914.36218 v 3.5 h 4 v 4 h 3.5 v -7.5 z m -4,4 v 7.5 h 7.5 v -7.5 z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -46,97 +46,129 @@ export class MapPermissions {
return this.map
}
_editAnonymous(container) {
const fields = []
if (this.isOwner()) {
fields.push([
'options.edit_status',
{
handler: 'IntSelect',
label: translate('Who can edit'),
selectOptions: this.map.options.edit_statuses,
},
])
const builder = new U.FormBuilder(this, fields)
const form = builder.build()
container.appendChild(form)
if (this.options.anonymous_edit_url) {
DomUtil.createCopiableInput(
container,
translate('Secret edit link:'),
this.options.anonymous_edit_url
)
}
if (this.map.options.user?.id) {
// 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
)
}
}
}
_editWithOwner(container) {
const topFields = []
const collaboratorsFields = []
const fieldset = Utils.loadTemplate(
`<fieldset class="separator"><legend>${translate('Map')}</legend></fieldset>`
)
container.appendChild(fieldset)
if (this.isOwner()) {
topFields.push([
'options.edit_status',
{
handler: 'IntSelect',
label: translate('Who can edit'),
selectOptions: this.map.options.edit_statuses,
},
])
topFields.push([
'options.share_status',
{
handler: 'IntSelect',
label: translate('Who can view'),
selectOptions: this.map.options.share_statuses,
},
])
collaboratorsFields.push([
'options.owner',
{ handler: 'ManageOwner', label: translate("Map's owner") },
])
if (this.map.options.user?.teams?.length) {
collaboratorsFields.push([
'options.team',
{
handler: 'ManageTeam',
label: translate('Attach map to a team'),
teams: this.map.options.user.teams,
},
])
}
}
collaboratorsFields.push([
'options.editors',
{ handler: 'ManageEditors', label: translate("Map's editors") },
])
const builder = new U.FormBuilder(this, topFields)
const form = builder.build()
container.appendChild(form)
if (collaboratorsFields.length) {
const fieldset = Utils.loadTemplate(
`<fieldset class="separator"><legend>${translate('Manage collaborators')}</legend></fieldset>`
)
container.appendChild(fieldset)
const builder = new U.FormBuilder(this, collaboratorsFields)
const form = builder.build()
container.appendChild(form)
}
}
_editDatalayers(container) {
if (this.map.hasLayers()) {
const fieldset = Utils.loadTemplate(
`<fieldset class="separator"><legend>${translate('Datalayers')}</legend></fieldset>`
)
container.appendChild(fieldset)
this.map.eachDataLayer((datalayer) => {
datalayer.permissions.edit(fieldset)
})
}
}
edit() {
if (this.map.options.editMode !== 'advanced') return
if (!this.map.options.umap_id) {
return Alert.info(translate('Please save the map first'))
Alert.info(translate('Please save the map first'))
return
}
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") },
])
if (this.map.options.user?.teams?.length) {
fields.push([
'options.team',
{
handler: 'ManageTeam',
label: translate('Attach map to a team'),
teams: this.map.options.user.teams,
},
])
}
}
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)
})
if (this.isAnonymousMap()) this._editAnonymous(container)
else this._editWithOwner(container)
this._editDatalayers(container)
this.map.editPanel.open({ content: container, className: 'dark' })
}
@ -157,8 +189,9 @@ export class MapPermissions {
for (let i = 0; i < this.options.editors.length; i++)
formData.append('editors', this.options.editors[i].id)
}
if (this.isOwner() || this.isAnonymousMap())
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('team', this.options.team?.id || '')

View file

@ -1029,6 +1029,7 @@ L.FormBuilder.ManageOwner = L.FormBuilder.Element.extend({
const options = {
className: 'edit-owner',
on_select: L.bind(this.onSelect, this),
placeholder: L._("Type new owner's username"),
}
this.autocomplete = new U.AjaxAutocomplete(this.parentNode, options)
const owner = this.toHTML()
@ -1058,6 +1059,7 @@ L.FormBuilder.ManageEditors = L.FormBuilder.Element.extend({
className: 'edit-editors',
on_select: L.bind(this.onSelect, this),
on_unselect: L.bind(this.onUnselect, this),
placeholder: L._("Type editor's username"),
}
this.autocomplete = new U.AjaxAutocompleteMultiple(this.parentNode, options)
this._values = this.toHTML()

View file

@ -724,6 +724,10 @@ U.Map = L.Map.extend({
}
},
hasLayers: function () {
return Boolean(this.datalayers_index.length)
},
fitDataBounds: function () {
const bounds = this.getLayersBounds()
if (!this.hasData() || !bounds.isValid()) return false

View file

@ -820,7 +820,6 @@ a.umap-control-caption,
.umap-browser .off .feature {
display: none;
}
.umap-facet-search .formbox,
.umap-browser .datalayer {
margin-bottom: 2px;
border-radius: 2px;
@ -831,7 +830,7 @@ a.umap-control-caption,
.umap-browser.dark .datalayer ul {
border: 1px solid #232729;
}
.umap-browser h5, .umap-facet-search h5 {
.umap-browser h5 {
margin-bottom: 0;
overflow: hidden;
padding-inline-start: 5px;
@ -849,7 +848,7 @@ a.umap-control-caption,
.umap-browser h5 span {
margin-inline-start: 10px;
}
.umap-browser li, .umap-facet-search li {
.umap-browser li {
padding: 2px 0;
white-space: nowrap;
overflow: hidden;