Skip to content

Instantly share code, notes, and snippets.

@esimonetti
Last active November 30, 2023 03:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save esimonetti/f42cacd2b59693fe6f689766d2592434 to your computer and use it in GitHub Desktop.
Save esimonetti/f42cacd2b59693fe6f689766d2592434 to your computer and use it in GitHub Desktop.
SugarCRM v7 - Conditional record locking through ACL's customisation - Reach out for SugarCRM consulting: https://www.naonis.tech/services/sugar-crm.html
<?php
// file: custom/Extension/modules/Cases/Ext/Vardefs/acl.php
$dictionary['Case']['acls']['SugarACLLock'] = true;
<?php
//
// Enrico Simonetti
// enricosimonetti.com
//
// 2016-05-08 on Sugar 7.7.0.0
// file: custom/data/acl/SugarACLLock.php
//
// ACL to lock record in certain conditions. Always allow admin users and specific user ids
//
// Changes to this class might require the user browser's storage and cache to be cleared, to work correctly
//
class SugarACLLock extends SugarACLStrategy
{
// allowed user ids
protected $user_ids_to_allow = array(
);
// denied actions
protected $denied_actions = array(
'edit',
'delete',
'massupdate',
'import',
);
// has to match on all conditions
protected $bean_lock_mappings = array(
'Cases' => array('status' => 'Closed', 'type_c' => 'Technical Support'),
);
// our custom method to check permissions
protected function _canUserWrite($context)
{
// retrieve user from context
$user = $this->getCurrentUser($context);
if($user->isAdmin() || in_array($user->id, $this->user_ids_to_allow)) {
return true;
} else {
// check additional beans conditions here
// is there a bean?
if(!empty($context['bean']) && is_object($context['bean']) && !empty($context['bean']->id)) {
// do we have a mapped known object instance?
if($context['bean'] instanceof SugarBean && !empty($context['bean']->module_dir) && !empty($this->bean_lock_mappings[$context['bean']->module_dir])) {
// do we have all matching fields for this object?
foreach($this->bean_lock_mappings[$context['bean']->module_dir] as $field => $value) {
// check if field is empty, as its value might not be loaded
if(empty($context['bean']->$field)) {
// retrieve the full bean as in some occastions (like listviews), not all fields are available on the context
$bean = BeanFactory::getBean($context['bean']->module_dir, $context['bean']->id);
} else {
$bean = $context['bean'];
}
if(empty($bean->$field) || $bean->$field != $value) {
// all conditions have to match, if any field is different from the condition or empty, allow
return true;
}
}
// default do not allow once here
return false;
}
}
return true;
}
}
// runtime access check
public function checkAccess($module, $view, $context)
{
$view = SugarACLStrategy::fixUpActionName($view);
// if it is not a blocked action, or there is no bean, allow it
if(!in_array($view, $this->denied_actions) || !isset($context['bean'])) {
return true;
}
// can user write?
if($this->_canUserWrite($context)) return true;
// everyone else for everything else is denied
return false;
}
// mostly for front-end access checks (cached on the application, per user)
public function getUserAccess($module, $access_list = array(), $context = array())
{
// retrieve original ACL
$acl = parent::getUserAccess($module, $access_list, $context);
// if user can't write
if(!$this->_canUserWrite($context)) {
// override access, disable access where required if not admin and not special user
foreach($acl as $access => $value) {
if(in_array($access, $this->denied_actions)) {
$acl[$access] = 0;
}
}
}
// return modified acl
return $acl;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment