I want my users to be able to view and edit only collection posts they have created. Is this something I would implement in the config file or programmatically? If so, any help would be much appreciated. Also, I will have a hidden boolean field created on each collection post only accessible by admions. Once that field is set to true only admins should be able to see/edit that post. This access to author’s own content will be more restrictive than just user groups.
Add this code to the read permissions of your collection to restrict entries by owner:
<?php
if ($context->user) {
$context->options['filter']['_by'] = $context->user['_id'];
}
Add this code to the read permissions of your collection to restrict entries by owner, except admins:
<?php
if ($context->user && $context->user['group'] != 'admin') {
$context->options['filter']['_by'] = $context->user['_id'];
}
Filtering + Hiding fields for non-admins can be done this way:
<?php
if ($context->user && $context->user['group'] != 'admin') {
// filter by admin-boolean
$context->options['filter']['admin_boolean'] = false;
// hide field for non-admin --> or use field acl instead
$context->options['fields']['admin_boolean'] = false;
}
I tried to test the solution above, but I have some weird bugs with boolean fields right now. I have to check in the next days, if I broke my test cockpit or if it is a real bug…
edit: I tried it again with a fresh installation and it worked.
Instead of adding $context->options['fields']['admin_boolean'] = false;
to the read permissions, you can add permissions in the context menu of your field to group “admin”. It has the same effect.
For some more permission options, you may have a look at
and
Thank you for your explanation. For some reason, I thought that code had to go into the update permissions as well which ended up disabling the desired functionality. With regards to the boolean field, I have already restricted the viewing and editing of it to admins. The boolean field name is published. Once published is set to true, I would like for all collection posts with a value of published: true to only be edited by admins. Is there a way to implement this functionality? Thank you so much for your help!
Hmm…, I’m not sure, if this is possible with collections permissions.
You could add this code to config/bootstrap.php
to interrupt the saving process. But it feels a bit wrong…
<?php
$app->on('collections.save.before.yourcollectionname', function($name, &$entry, $isUpdate) {
if ($isUpdate && $entry['published'] == true && $this->module('cockpit')->getGroup() != 'admin') {
die;
}
});
Thank you for your help. That code did the trick. I like the way you put it that “it feels a bit wrong.” Could the problem be in the way I am imagining how to handle published content? Is there a way to hide content with published:true from all users except admins? I could create a new collection only visible and editable by admins, but unless I am mistaken that would involve an admin manually porting content from one collection to another which doesn’t seem right. I am trying to use published:true as a way to designate which collection posts will generate website content. Once the content has been “approved” by admin to be published to the website, I do not want anyone but an admin to be able to edit it in any way. I am building a Vue app that uses axios to get content from Cockpit with the filter:{published:true}. This code does what I asked, and I really appreciate it. I may just replace the saving failed message with a custom one to alert users as to why they cannot update the specific post. Cheers!
This was a hard one, but I’m sure, I’ll need it myself in the future.
And I also found another way while fiddling. You don’t need the extra bootstrap.php.
read permissions:
<php
if ($context->user && $context->user['group'] != 'admin') {
// filter by content owner
$context->options['filter']['_by'] = $context->user['_id'];
// return error when trying to edit published entries
if (isset($context->entry) && (!isset($context->entry['published']) || $context->entry['published']) == true) {
return cockpit()->stop('{"error": "You can\'t edit published entries."}', 401);
}
}
delete permissions:
if ($context->user && $context->user['group'] != 'admin') {
$entries = cockpit()->module('collections')->find($collection['name'], $context->options);
foreach ($entries as $entry) {
if ($entry['published'] == true) {
return cockpit()->stop('{"error": "You can\'t delete published entries."}', 401);
}
}
}
Wait with the delete permissions until this PR got merged. Right now, the delete permission has no effect.
Full example with annotations
Just a hint:
Instead of
return cockpit()->stop('{"error": "You can\'t delete published entries."}', 401);
you can also do this:
return 401;
These tips were awesome. Thank you so much for all the help!
Sorry to revive this old thread, but I would need the code to restrict update permissions. Is it the same as for delete?
@artur is there any equivalent of this for Cockpit v2?
CC: @raffaelj, I saw that all your snippets are for v1 only and supposedly do not work with v2
Just sharing what I used to do in Cockpit V2.
- If the request is from
admin
, he/she can perform delete and update. - If the request is from user, allow to perform
create
but restrictdelete
andupdate
(especially protect from overwriting other user’s content)
Note: this affects all models.
// Before remove
$this->on('content.remove.before', function ($modelName, &$filter, $collection) {
$user = $this->helper('auth')->getUser();
if (!$user) {
return $this->stop(404);
}
$account = $this->dataStorage->findOne('system/users', ['user' => $user['user']]);
// Protect only when role is user
if ($user['role'] === 'user') {
if ($account['_id'] !== $item['_cby']) {
return $this->stop('{"error": "You can\'t perform this action."}', 401);
}
}
});
// Before create or update
$this->on('content.item.save.before', function ($modelName, &$item, $isUpdate, $collection) {
$user = $this->helper('auth')->getUser();
if (!$user) {
return $this->stop(404);
}
$account = $this->dataStorage->findOne('system/users', ['user' => $user['user']]);
// Protect only when role is user and update
if ($user['role'] === 'user' && $isUpdate === true) {
if ($account['_id'] !== $item['_mby']) {
return $this->stop('{"error": "You can\'t perform this action."}', 401);
}
}
// Just make sure when creating a new item, it has _cby and _mby
$item['_cby'] = $account['_id'];
$item['_mby'] = $account['_id'];
});
Hope this helps someone.