[Back] <?php
class OsController {
protected $params,
$files,
$layout = 'admin',
$views_folder = LATEPOINT_VIEWS_ABSPATH_SHARED,
$return_format = 'html',
$extra_css_classes = ['latepoint'];
public array $fields_to_update = [];
// if an action can only be accessed by a backend user, we need to define capabilities that are required
public array $controller_capabilities = ['settings__edit']; // default for controller
public array $action_capabilities = []; // per action
public array $action_access = [ 'customer' => [], 'public' => [] ];
public $vars;
public $route_name;
function __construct(){
$this->params = $this->get_params();
$this->files = $this->get_files();
$this->set_layout($this->layout);
$this->vars['page_header'] = __('Bookings', 'latepoint');
$this->vars['breadcrumbs'][] = array('label' => __('Dashboard', 'latepoint'), 'link' => OsRouterHelper::build_link(['dashboard', 'index'] ));
$this->load_settings();
$this->vars['logged_in_customer'] = OsAuthHelper::get_logged_in_customer();
}
public function check_nonce($action, $custom_nonce = ''){
$nonce = !empty($custom_nonce) ? $custom_nonce : $this->params['_wpnonce'];
if(!wp_verify_nonce($nonce, $action)){
if($this->get_return_format() == 'json'){
$this->send_json(array('status' => LATEPOINT_STATUS_ERROR, 'message' => __('Invalid Request', 'latepoint')));
}else{
wp_die();
}
}
}
public function can_current_user_access_action(string $action): bool{
if(in_array($action, $this->action_access['public'])){
// public route
$can = true;
}elseif(in_array($action, $this->action_access['customer']) && OsAuthHelper::get_current_user()->customer){
// customer route & customer is logged in
$can = true;
}else{
// backend route, check for capabilities
$can = OsAuthHelper::get_current_user()->has_capability($this->get_capabilities_required_for_action($action));
}
/**
* Determines if a currently logged in user can access controller's action
*
* @since 4.7.0
* @hook latepoint_can_current_user_access_action
*
* @param {bool} $can Decision true|false
* @param {string} $action Name of the action that is being called
* @param {LatePoint\Misc\User} $current_user Currently logged in latepoint user
* @returns {bool} Decision true|false
*/
return apply_filters('latepoint_can_current_user_access_action', $can, $action, OsAuthHelper::get_current_user());
}
public function get_capabilities_required_for_action($action){
return OsRolesHelper::get_capabilities_required_for_controller_action(get_class($this), $action);
}
function generate_css_class($view_name){
$class_name_filtered = strtolower(preg_replace('/^Os(\w+)Controller/i', '$1', static::class));
return "latepoint-view-{$class_name_filtered}-{$view_name}";
}
protected function load_settings(){
}
public function access_not_allowed(){
$this->format_render(__FUNCTION__, [], [], true);
exit();
}
function format_render($view_name, $extra_vars = array(), $json_return_vars = array(), $from_shared_folder = false){
echo $this->format_render_return($view_name, $extra_vars, $json_return_vars, $from_shared_folder);
}
// You can pass array to $view_name, ['json_view_name' => ..., 'html_view_name' => ...]
function format_render_return($view_name, $extra_vars = array(), $json_return_vars = array(), $from_shared_folder = false){
$html = '';
if($this->get_return_format() == 'json'){
if(is_array($view_name)) $view_name = $view_name['json_view_name'];
$response_html = $this->render($this->get_view_uri($view_name, $from_shared_folder), 'none', $extra_vars);
$this->send_json(array_merge(array('status' => LATEPOINT_STATUS_SUCCESS, 'message' => $response_html), $json_return_vars));
}else{
if(is_array($view_name)) $view_name = $view_name['html_view_name'];
$this->extra_css_classes[] = $this->generate_css_class($view_name);
$this->vars['extra_css_classes'] = $this->extra_css_classes;
$html = $this->render($this->get_view_uri($view_name, $from_shared_folder), $this->get_layout(), $extra_vars);
}
return $html;
}
function set_layout($layout = 'admin'){
if(isset($this->params['layout'])){
$this->layout = $this->params['layout'];
}else{
$this->layout = $layout;
}
}
function get_layout(){
return $this->layout;
}
function set_return_format($format = 'html'){
$this->return_format = $format;
}
function get_return_format(){
return $this->return_format;
}
function send_json($data, $status_code = null){
if(!empty($this->fields_to_update)) $data['fields_to_update'] = $this->fields_to_update;
wp_send_json($data, $status_code);
}
function get_view_uri($view_name, $from_shared_folder = false){
if($from_shared_folder){
$view_uri = LATEPOINT_VIEWS_ABSPATH_SHARED.$view_name.'.php';
}else{
$view_uri = $this->views_folder.$view_name.'.php';
}
return $view_uri;
}
private function get_safe_layout_path($layout) {
// 1. Remove any path separators and null bytes
$layout = str_replace(['/', '\\', "\0"], '', $layout);
// 2. Remove any dots to prevent directory traversal
$layout = str_replace('.', '', $layout);
// 3. Only allow alphanumeric, underscore, and hyphen
$layout = preg_replace('/[^a-zA-Z0-9_-]/', '', $layout);
// 4. Construct the full path
$layout_file = $this->add_extension($layout, '.php');
$full_path = LATEPOINT_VIEWS_LAYOUTS_ABSPATH . $layout_file;
// 5. Use realpath to resolve any remaining traversal attempts
$real_path = realpath($full_path);
$base_path = realpath(LATEPOINT_VIEWS_LAYOUTS_ABSPATH);
// 6. Ensure the resolved path is within the layouts directory
if ($real_path && $base_path && strpos($real_path, $base_path) === 0) {
return $real_path;
}
return false;
}
// render view and if needed layout, when layout is rendered - view variable is passed to a layout file
function render($view, $layout = 'none', $extra_vars = array()){
$this->vars['route_name'] = $this->route_name;
extract($extra_vars);
extract($this->vars);
ob_start();
if($layout != 'none'){
$layout_path = $this->get_safe_layout_path($layout);
// rendering layout, view variable will be passed and used in layout file
if($layout_path){
include $layout_path;
}else{
__('Invalid layout', 'latepoint');
}
}else{
include $this->add_extension($view, '.php');
}
$response_html = ob_get_clean();
return $response_html;
}
/*
Adds extension to a file string if its missing
*/
function add_extension($string = '', $extension = '.php'){
if(substr($string, -strlen($extension))===$extension) return $string;
else return $string.$extension;
}
function get_files(){
return OsParamsHelper::get_files();
}
function get_params(){
return OsParamsHelper::get_params();
}
}