<?php
namespace Phad;
trait Can_Do {
public $force_allow_access = false;
/**
* Check an access string.
* @alias can_access just to make it easier to simply check a string
*
* @param string a string like 'role:admin;call:whatever;'
* @param $ItemInfo optional array of ItemInfo from a compiled phad item
*
* @return true/false
*
* @throws exception if a function is not defined.
*/
public function can(string $function, $ItemInfo=null){
return $this->can_access(['access'=>$function], $ItemInfo);
}
/**
* @param $node array of the node's attributes & it's tagName
*/
public function can_read_node(array $node){
if (!isset($node['access'])){return true;}
return $this->can_access($node);
}
/**
* Checks if the data node can be accessed.
*
* @param $node the data node (as an array)
* @param $Info item info object
* @return true/false
*/
public function can_read_data(array $node, \Phad\ItemInfo $Info): bool {
if ($Info->mode==\Phad\Blocks::FORM_SUBMIT&&$node['type']!='default')return false;
if (!isset($node['access'])){
return true;
}
return $this->can_access($node);
}
/**
* Check an access string
*
* @param array $node a nodeinfo array containing a key 'access' with a string like 'role:admin;call:whatever;'
* @param $ItemInfo optional array of ItemInfo from a compiled phad item
*
* @return true/false
*
* @throws exception if a function is not defined.
*/
public function can_access(array $node, $ItemInfo=null){
$access_string = $node['access'];
$functions = $this->parse_functions($access_string);
if ($this->force_allow_access === true)return true;
foreach ($functions as $fn=>/*string*/$arg){
// var_dump("Function:".$fn);
if ($fn=='role'){
if (!$this->access->user_has_role($arg))return false;
} else if ($fn=='request_method'){
if (strtolower($_SERVER['REQUEST_METHOD'])!=strtolower($arg))return false;
} else if ($fn=='call'){
//@todo append to `$Info->errors[]` ??
if($this->access->call(\Phad\Caller::CAN_ACCESS, $arg, $ItemInfo, $node)!==true)return false;
} else if ($fn=='std'){
if ($arg=='false')return false;
else if ($arg=='true')return true;
else {
throw new \Exception(sprintf(\Phad\Errors::STD_ACCESS_FUNCTION_NOT_EXIST, $fn, $arg, $access_string));
}
} else if ($fn=="fn"){
if (function_exists($arg)){
$ret = $arg();
if (!is_bool($ret)){
throw new \Exception("PHP function '$arg' MUST return strict boolean `true` or `false` when used as an access control");
}
return $ret;
} else {
throw new \Exception("PHP function '$arg' does not exist, so '$fn:$arg' cannot be processed");
}
} else {
throw new \Exception("'$fn:$arg' cannot be processed because access control prefix '$fn' is not supported.");
}
}
return true;
}
public function can_delete($Info){
$candelete = $Info->candelete;
//@todo test the different branches for can_delete() & delete()
if (!isset($Info->args['id'])){
$Info->submit_errors[] = ['msg'=>"Cannot delete '".$Info->name."' without an id. No id was submitted."];
return false;
} else if ($candelete===true)return true;
else if ($candelete===false){
$Info->submit_errors[] = ['msg'=>"Deletion of '".$Info->name."' is disabled."];
return false;
}
$functions = $this->parse_functions($Info->candelete);
foreach ($functions as $fn=> /* string */ $args){
if ($fn=='role'){
if (!$this->user_has_role($args)){
$Info->submit_errors[] = ['msg'=>"Deletion of '".$Info->name."' not allowed unless you have role(s): '$args'"];
return false;
}
}
else if ($fn=='call'){
$count = count($Info->submit_errors);
$did_pass = $this->access->call(\Phad\Caller::CAN_DELETE, $args, $Info);
if ($did_pass!==true){
if (count($Info->submit_errors)==$count){
$item_name = $Info->name;
$Info->submit_errors[] = ['msg'=>"Deleting '$item_name' rejected by '$fn:$args'"];
}
return false;
}
}
}
return true;
}
public function can_submit(\Phad\ItemInfo $Info, $ItemRow){
//@todo test the different branches for can_delete() & delete()
if (isset($Info->cancel_submit)&&$Info->cancel_submit === true){
// var_dump($Info->cansubmit);
return false;
}
$functions = $this->parse_functions($Info->cansubmit);
foreach ($functions as $fn=> /* string */ $args){
if ($fn=='role'){
if (!$this->user_has_role($args)){
$Info->submit_errors[] = ['msg'=>"Deletion of '".$Info->name."' not allowed unless you have role(s): '$args'"];
return false;
}
}
else if ($fn=='call'){
$count = count($Info->submit_errors);
$did_pass = $this->access->call(\Phad\Caller::CAN_SUBMIT, $args, $Info, $ItemRow);
if ($did_pass!==true){
if (count($Info->submit_errors)==$count){
$item_name = $Info->name;
$Info->submit_errors[] = ['msg'=>"Deleting '$item_name' rejected by '$fn:$args'"];
}
return false;
}
}
}
return true;
}
}