mirror of
https://github.com/appwrite/appwrite
synced 2026-05-22 16:38:32 +00:00
Merge pull request #3729 from appwrite/fix-permissions-ui
Fix + improve permissions UI
This commit is contained in:
commit
faaf233153
13 changed files with 67 additions and 41 deletions
|
|
@ -294,6 +294,7 @@ App::get('/console/databases/collection')
|
|||
$permissions
|
||||
->setParam('method', 'databases.getCollection')
|
||||
->setParam('events', 'load,databases.updateCollection')
|
||||
->setParam('form', 'collectionPermissions')
|
||||
->setParam('data', 'project-collection')
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.id}}',
|
||||
|
|
@ -329,7 +330,6 @@ App::get('/console/databases/document')
|
|||
->action(function (string $databaseId, string $collection, View $layout) {
|
||||
|
||||
$logs = new View(__DIR__ . '/../../views/console/comps/logs.phtml');
|
||||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
->setParam('method', 'databases.listDocumentLogs')
|
||||
|
|
@ -341,15 +341,16 @@ App::get('/console/databases/document')
|
|||
;
|
||||
|
||||
$permissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
|
||||
$permissions
|
||||
->setParam('method', 'databases.getDocument')
|
||||
->setParam('events', 'load,databases.updateDocument')
|
||||
->setParam('form', 'documentPermissions')
|
||||
->setParam('data', 'project-document')
|
||||
->setParam('permissions', \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($perm) => $perm != Database::PERMISSION_CREATE
|
||||
))
|
||||
->setParam('permissions', [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
])
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.collection}}',
|
||||
'database-id' => '{{router.params.databaseId}}',
|
||||
|
|
@ -384,10 +385,12 @@ App::get('/console/databases/document/new')
|
|||
|
||||
$permissions
|
||||
->setParam('data', 'project-document')
|
||||
->setParam('permissions', \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($perm) => $perm != Database::PERMISSION_CREATE
|
||||
))
|
||||
->setParam('form', 'documentPermissions')
|
||||
->setParam('permissions', [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
])
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.collection}}',
|
||||
'database-id' => '{{router.params.databaseId}}',
|
||||
|
|
@ -451,20 +454,22 @@ App::get('/console/storage/bucket')
|
|||
$fileCreatePermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
$fileCreatePermissions
|
||||
->setParam('form', 'fileCreatePermissions')
|
||||
->setParam('permissions', \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($perm) => $perm != Database::PERMISSION_CREATE
|
||||
));
|
||||
->setParam('permissions', [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
]);
|
||||
|
||||
$fileUpdatePermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
|
||||
$fileUpdatePermissions
|
||||
->setParam('method', 'storage.getFile')
|
||||
->setParam('data', 'file')
|
||||
->setParam('form', 'fileUpdatePermissions')
|
||||
->setParam('permissions', \array_filter(
|
||||
Database::PERMISSIONS,
|
||||
fn ($perm) => $perm != Database::PERMISSION_CREATE
|
||||
))
|
||||
->setParam('permissions', [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
])
|
||||
->setParam('params', [
|
||||
'bucket-id' => '{{router.params.id}}',
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ $params = $this->getParam('params', []);
|
|||
$events = $this->getParam('events', '');
|
||||
$permissions = $this->getParam('permissions', Database::PERMISSIONS);
|
||||
$data = $this->getParam('data', '');
|
||||
$form = $this->getParam('form', 'form');
|
||||
$form = $this->getParam('form');
|
||||
|
||||
$escapedPermissions = \array_map(function ($perm) {
|
||||
// Alpine won't bind to a parameter named delete
|
||||
|
|
@ -34,7 +34,7 @@ $escapedPermissions = \array_map(function ($perm) {
|
|||
<?php if (!empty($data)): ?>
|
||||
data-name="<?php echo $data; ?>"
|
||||
<?php endif; ?>
|
||||
@reset.window="permissions = rawPermissions = []">
|
||||
@reset.window="permissions.length = rawPermissions.length = 0">
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
|
|
@ -79,7 +79,7 @@ $escapedPermissions = \array_map(function ($perm) {
|
|||
</tr>
|
||||
</template>
|
||||
<tr x-data="permissionsRow"
|
||||
@addrow.window="addPermission('<?php echo $form; ?>',role,{<?php echo \implode(',', $escapedPermissions) ?>})">
|
||||
@addrow<?php echo \strtolower($form); ?>.window="addPermission('<?php echo $form; ?>', role, { <?php echo \implode(',', $escapedPermissions) ?> })">
|
||||
<td>
|
||||
<datalist id="types">
|
||||
<option value="user:">
|
||||
|
|
@ -91,7 +91,7 @@ $escapedPermissions = \array_map(function ($perm) {
|
|||
|
||||
<input
|
||||
required
|
||||
id="<?php echo $form; ?>"
|
||||
id="<?php echo $form; ?>Input"
|
||||
name="<?php echo $form; ?>"
|
||||
form="<?php echo $form ?>"
|
||||
list="types"
|
||||
|
|
@ -109,7 +109,7 @@ $escapedPermissions = \array_map(function ($perm) {
|
|||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="<?php \count($permissions) + 2 ?>">
|
||||
<button type="button" class="btn btn-primary margin-top-small" @click="$dispatch('addrow')">Add</button>
|
||||
<button type="button" class="margin-top-small reverse" @click="$dispatch('addrow<?php echo \strtolower($form); ?>')">Add</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ $permissions = $this->getParam('permissions', null);
|
|||
|
||||
<div class="row responsive margin-top-negative">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form id="<?php echo $permissions->getParam('form', 'permissions') ?>"></form>
|
||||
<form id="<?php echo $permissions->getParam('form') ?>"></form>
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ $permissions = $this->getParam('permissions', null);
|
|||
|
||||
<div class="row responsive">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form id="<?php echo $permissions->getParam('form', 'permissions') ?>"></form>
|
||||
<form id="<?php echo $permissions->getParam('form') ?>"></form>
|
||||
|
||||
<form
|
||||
data-analytics
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ $fileUpdatePermissions = $this->getParam('fileUpdatePermissions', null);
|
|||
<td class="col-name" data-title="Name: " data-ls-attrs="title={{file.name}}" >
|
||||
<div class="trim-inner-text">
|
||||
<span data-ls-ui-trigger="modal-file-update-{{file.$id}}" class="link text-one-liner" data-ls-bind="{{file.name}}"></span>
|
||||
<div data-ls-attrs="data-open-event=modal-file-update-{{file.$id}}" data-button-hide="1" data-ui-modal class="box modal sticky-footer width-large close" >
|
||||
<div data-ls-attrs="data-open-event=modal-file-update-{{file.$id}}" data-button-hide="1" data-ui-modal class="box modal width-xlarge sticky-footer close" >
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1>Update File</h1>
|
||||
|
|
@ -139,12 +139,11 @@ $fileUpdatePermissions = $this->getParam('fileUpdatePermissions', null);
|
|||
</div>
|
||||
<input type="hidden" data-ls-attrs="id=file-bucketId-{{file.$id}}" name="bucketId" data-ls-bind="{{file.bucketId}}">
|
||||
|
||||
<div class="toggle margin-bottom" data-ls-if="{{project-bucket.fileSecurity}}" data-ls-ui-open data-button-aria="Open Permissions">
|
||||
<i class="icon-plus pull-end margin-top-tiny"></i>
|
||||
<i class="icon-minus pull-end margin-top-tiny"></i>
|
||||
<hr class="margin-start margin-end" />
|
||||
|
||||
<h3 class="margin-bottom-large">Permissions</h3>
|
||||
<h3 class="margin-bottom">Permissions</h3>
|
||||
|
||||
<div class="margin-start margin-end margin-bottom">
|
||||
<?php echo $fileUpdatePermissions->render(); ?>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
2
composer.lock
generated
2
composer.lock
generated
|
|
@ -5391,5 +5391,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
|
|
|||
6
public/dist/scripts/app-all.js
vendored
6
public/dist/scripts/app-all.js
vendored
|
|
@ -4054,11 +4054,13 @@ if(this.attribute){event+=`.${this.attribute}`;}
|
|||
this.events.add(event);this.reset();},removeEvent(value){this.events.delete(value);}}));});})(window);(function(window){document.addEventListener('alpine:init',()=>{Alpine.data('permissionsMatrix',()=>({permissions:[],rawPermissions:[],load(permissions){if(permissions===undefined){return;}
|
||||
this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePermission(p);type=this.parseInputPermission(type);let index=-1;let existing=this.permissions.find((p,idx)=>{if(p.role===role){index=idx;return true;}})
|
||||
if(existing===undefined){this.permissions.push({role,[type]:true,});}
|
||||
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!document.getElementById(formId).reportValidity()){return;}
|
||||
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!this.validate(formId,role,permissions)){return;}
|
||||
Object.entries(permissions).forEach(entry=>{let[type,enabled]=entry;type=this.parseOutputPermission(type);if(enabled){this.rawPermissions.push(this.buildPermission(type,role));}});this.permissions.push({role,...permissions,});this.reset();},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;}
|
||||
const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';}
|
||||
return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';}
|
||||
return key;}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
|
||||
return key;},validate(formId,role,permissions){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input`);input.setCustomValidity('');if(!Object.values(permissions).some(p=>p)){input.setCustomValidity('No permissions selected');}
|
||||
if(this.permissions.some(p=>p.role===role)){input.setCustomValidity('Role entry already exists');}
|
||||
return form.reportValidity();}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
|
||||
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},6000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;}
|
||||
router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
|
||||
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
|
||||
|
|
|
|||
6
public/dist/scripts/app.js
vendored
6
public/dist/scripts/app.js
vendored
|
|
@ -609,11 +609,13 @@ if(this.attribute){event+=`.${this.attribute}`;}
|
|||
this.events.add(event);this.reset();},removeEvent(value){this.events.delete(value);}}));});})(window);(function(window){document.addEventListener('alpine:init',()=>{Alpine.data('permissionsMatrix',()=>({permissions:[],rawPermissions:[],load(permissions){if(permissions===undefined){return;}
|
||||
this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePermission(p);type=this.parseInputPermission(type);let index=-1;let existing=this.permissions.find((p,idx)=>{if(p.role===role){index=idx;return true;}})
|
||||
if(existing===undefined){this.permissions.push({role,[type]:true,});}
|
||||
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!document.getElementById(formId).reportValidity()){return;}
|
||||
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!this.validate(formId,role,permissions)){return;}
|
||||
Object.entries(permissions).forEach(entry=>{let[type,enabled]=entry;type=this.parseOutputPermission(type);if(enabled){this.rawPermissions.push(this.buildPermission(type,role));}});this.permissions.push({role,...permissions,});this.reset();},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;}
|
||||
const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';}
|
||||
return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';}
|
||||
return key;}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
|
||||
return key;},validate(formId,role,permissions){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input`);input.setCustomValidity('');if(!Object.values(permissions).some(p=>p)){input.setCustomValidity('No permissions selected');}
|
||||
if(this.permissions.some(p=>p.role===role)){input.setCustomValidity('Role entry already exists');}
|
||||
return form.reportValidity();}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
|
||||
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},6000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;}
|
||||
router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
|
||||
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
|
||||
|
|
|
|||
2
public/dist/styles/default-ltr.css
vendored
2
public/dist/styles/default-ltr.css
vendored
File diff suppressed because one or more lines are too long
2
public/dist/styles/default-rtl.css
vendored
2
public/dist/styles/default-rtl.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -34,7 +34,7 @@
|
|||
});
|
||||
},
|
||||
addPermission(formId, role, permissions) {
|
||||
if (!document.getElementById(formId).reportValidity()) {
|
||||
if (!this.validate(formId, role, permissions)) {
|
||||
return;
|
||||
}
|
||||
Object.entries(permissions).forEach(entry => {
|
||||
|
|
@ -105,6 +105,21 @@
|
|||
return 'delete';
|
||||
}
|
||||
return key;
|
||||
},
|
||||
validate(formId, role, permissions) {
|
||||
const form = document.getElementById(formId);
|
||||
const input = document.getElementById(`${formId}Input`);
|
||||
|
||||
input.setCustomValidity('');
|
||||
|
||||
if (!Object.values(permissions).some(p => p)) {
|
||||
input.setCustomValidity('No permissions selected');
|
||||
}
|
||||
if (this.permissions.some(p => p.role === role)) {
|
||||
input.setCustomValidity('Role entry already exists');
|
||||
}
|
||||
|
||||
return form.reportValidity();
|
||||
}
|
||||
}));
|
||||
Alpine.data('permissionsRow', () => ({
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@
|
|||
max-width: 800px;
|
||||
}
|
||||
|
||||
&.width-xlarge {
|
||||
max-width: 950px;
|
||||
}
|
||||
|
||||
&.open {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
.permissions-matrix {
|
||||
th:first-child, td:first-child {
|
||||
width: 100px;
|
||||
width: 35%;
|
||||
}
|
||||
th:not(:first-child):not(:last-child), td:not(:first-child):not(:last-child) {
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
th:last-child, td:last-child {
|
||||
width: 20px;
|
||||
width: 10%;
|
||||
}
|
||||
td {
|
||||
vertical-align: middle;
|
||||
|
|
|
|||
Loading…
Reference in a new issue