<?php /* * Copyright (c) 2023 LatePoint LLC. All rights reserved. */ namespace LatePoint\Misc; class User{ public ?string $backend_user_type = null; public ?\WP_User $wp_user = null; public array $roles = []; public ?\OsAgentModel $agent = null; public ?\OsCustomerModel $customer = null; public ?string $wp_capability = null; protected array $allowed_records = ['agent' => [], 'service' => [], 'location' => []]; protected array $capabilities = []; function __construct(){ } public function get_wp_user_meta($meta_key, $default = ''){ $meta_value = $default; if($this->wp_user){ $meta_value = get_user_meta($this->wp_user->ID, $meta_key, true); if(empty($meta_value)) $meta_value = $default; } return $meta_value; } public function update_wp_user_meta($meta_key, $meta_value){ if($this->wp_user){ update_user_meta($this->wp_user->ID, $meta_key, $meta_value); } } /** * * Checks if user has a certain capability * * @param array|string $capability single capability or an array of capabilities to check if user has or not * @return bool */ public function has_capability($capability): bool{ if($this->has_backend_access()){ // only backend user types have capabilities, check if it's a backend user type first if(is_array($capability)){ $can = empty(array_diff($capability, $this->get_capabilities())); }else{ $can = in_array($capability, $this->get_capabilities()); } }else{ $can = false; } /** * Checks if a user has certain capability * * @since 4.7.0 * @hook latepoint_user_has_capability * * @param {bool} $can answer to a question if user has a capability * @param {array|string} $capability array or a single capability that needs to be checked * @returns {bool} answer to a question if user has a capability */ return apply_filters('latepoint_user_has_capability', $can, $capability); } public static function load_from_wp_user(\WP_User $wp_user): User{ $user = new self(); $user->wp_user = $wp_user; if(in_array('administrator', $wp_user->roles)){ // ADMIN $user->backend_user_type = LATEPOINT_USER_TYPE_ADMIN; $user->wp_capability = 'manage_options'; }elseif(in_array(LATEPOINT_WP_AGENT_ROLE, $wp_user->roles)){ // AGENT $user->backend_user_type = LATEPOINT_USER_TYPE_AGENT; $user->wp_capability = 'edit_bookings'; // get connected agent model $agent = new \OsAgentModel(); $agent = $agent->where(['wp_user_id' => $wp_user->ID])->set_limit(1)->get_results_as_models(); if($agent) $user->agent = $agent; }else{ // see if it's one of custom roles $custom_roles = \OsRolesHelper::get_custom_roles(); foreach($custom_roles as $custom_role){ if(!empty($wp_user->roles) && in_array($custom_role['wp_role'], $wp_user->roles)){ $user->backend_user_type = LATEPOINT_USER_TYPE_CUSTOM; $user->wp_capability = 'manage_latepoint'; break; } } } $user->set_roles(); $user->set_capabilities(); $user->set_allowed_records(); return $user; } public function set_roles(){ if(empty($this->wp_user)) return; if($this->wp_user->roles){ foreach($this->wp_user->roles as $role){ $this->roles[] = Role::get_from_wp_role($role); } } } /* * Check if user has custom permissions set to access actions, instead of using default ones attached to the role */ public function is_custom_capabilities(): bool{ return(!empty($this->get_custom_capabilities())); } /* * Check if user has custom access to model records, instead of using default ones attached to the role */ public function is_custom_allowed_records(): bool{ return(!empty($this->get_custom_allowed_records())); } // PERMITTED ACTIONS // ----------------- public function clear_custom_capabilities(){ return delete_user_meta($this->wp_user->ID, 'latepoint_custom_capabilities'); } public function set_custom_capabilities($capabilities){ return update_user_meta($this->wp_user->ID, 'latepoint_custom_capabilities', $capabilities); } public function get_custom_capabilities(){ return get_user_meta($this->wp_user->ID, 'latepoint_custom_capabilities', true); } public function get_capabilities(){ return $this->capabilities; } protected function set_capabilities(){ switch($this->backend_user_type){ case LATEPOINT_USER_TYPE_ADMIN: // admins always get all permissions $this->capabilities = \OsRolesHelper::get_all_available_actions_list(); break; case LATEPOINT_USER_TYPE_AGENT: case LATEPOINT_USER_TYPE_CUSTOM: $custom = $this->get_custom_capabilities(); if(empty($custom)){ foreach($this->roles as $role){ $this->capabilities = array_merge($this->capabilities, $role->get_capabilities()); } }else{ $this->capabilities = maybe_unserialize($custom); } break; } } // ALLOWED RECORDS // ----------------- public function clear_custom_allowed_records(){ return delete_user_meta($this->wp_user->ID, 'latepoint_custom_allowed_records'); } public function set_custom_allowed_records($allowed_records){ if(empty($allowed_records)){ return $this->clear_custom_allowed_records(); }else{ return update_user_meta($this->wp_user->ID, 'latepoint_custom_allowed_records', $allowed_records); } } public function get_custom_allowed_records(){ return get_user_meta($this->wp_user->ID, 'latepoint_custom_allowed_records', true); } public function get_allowed_records(string $model_type, $load_from_db = false){ if($load_from_db){ // do not calculate allowed records based on what connections are available, just load from database whatever is set $custom = $this->get_custom_allowed_records(); if($custom){ $custom_records = maybe_unserialize($custom); return !empty($custom_records[$model_type]) ? $custom_records[$model_type] : []; }else{ return LATEPOINT_ALL; } }else{ return !empty($this->allowed_records[$model_type]) ? $this->allowed_records[$model_type] : []; } } public function is_single_record_allowed(string $model_type): bool{ return $this->are_all_records_allowed($model_type) ? false : (count($this->get_allowed_records($model_type)) == 1); } public function are_all_records_allowed(string $model_type = '', $load_from_db = false): bool{ if($load_from_db && $model_type){ $allowed_records = $this->get_allowed_records($model_type, true); return ($allowed_records == LATEPOINT_ALL); }else{ if($model_type){ return ($this->allowed_records[$model_type] == LATEPOINT_ALL); }else{ return (($this->allowed_records['agent'] == LATEPOINT_ALL) && ($this->allowed_records['location'] == LATEPOINT_ALL) && ($this->allowed_records['service'] == LATEPOINT_ALL)); } } } public function check_if_allowed_record_id($id, string $model_type){ if(empty($id)) return false; if($this->are_all_records_allowed($model_type)) return $id; if(array_intersect([$id], $this->get_allowed_records($model_type))) return $id; return false; } public function clean_query_args($args){ $model_types = ['agent', 'service', 'location']; foreach($model_types as $model_type){ if(empty($args[$model_type.'_id']) && !$this->are_all_records_allowed($model_type)){ $args[$model_type.'_id'] = $this->get_allowed_records($model_type); } } return $args; } protected function set_allowed_records(){ switch($this->backend_user_type){ case LATEPOINT_USER_TYPE_ADMIN: $this->allowed_records['agent'] = LATEPOINT_ALL; $this->allowed_records['service'] = LATEPOINT_ALL; $this->allowed_records['location'] = LATEPOINT_ALL; break; case LATEPOINT_USER_TYPE_AGENT: if($this->agent){ $this->allowed_records['agent'] = [$this->agent->id] ?? []; $connection = new \OsConnectorModel(); $connections = $connection->where(['agent_id' => $this->agent->id])->get_results_as_models(); if($connections){ foreach($connections as $connection){ if(!in_array($connection->service_id, $this->allowed_records['service'])) $this->allowed_records['service'][] = $connection->service_id; if(!in_array($connection->location_id, $this->allowed_records['location'])) $this->allowed_records['location'][] = $connection->location_id; } } } break; case LATEPOINT_USER_TYPE_CUSTOM: // each user has their own settings, by default they allowed to access ALL, query DB to find custom settings $custom = $this->get_custom_allowed_records(); if(empty($custom)){ $this->allowed_records['agent'] = LATEPOINT_ALL; $this->allowed_records['service'] = LATEPOINT_ALL; $this->allowed_records['location'] = LATEPOINT_ALL; }else{ $custom_records = maybe_unserialize($custom); $model_types = ['agent', 'service', 'location']; $connection = new \OsConnectorModel(); // (e.g. if specific locations are selected - make sure agents and services are filtered to only have those that are connected to that location) foreach($model_types as $model_type){ if($custom_records[$model_type] != LATEPOINT_ALL){ $connection->where([$model_type.'_id' => $custom_records[$model_type]]); } } $allowed_connections = $connection->get_results_as_models(); foreach($allowed_connections as $allowed_connection){ if(!in_array($allowed_connection->agent_id, $this->allowed_records['agent'])) $this->allowed_records['agent'][] = $allowed_connection->agent_id; if(!in_array($allowed_connection->service_id, $this->allowed_records['service'])) $this->allowed_records['service'][] = $allowed_connection->service_id; if(!in_array($allowed_connection->location_id, $this->allowed_records['location'])) $this->allowed_records['location'][] = $allowed_connection->location_id; } } break; } } public function get_link_to_settings(): string{ switch($this->backend_user_type){ case LATEPOINT_USER_TYPE_ADMIN: return \OsRouterHelper::build_link(['settings', 'general']); case LATEPOINT_USER_TYPE_AGENT: return \OsRouterHelper::build_link(['agents', 'edit_form'], ['id' => $this->agent->id]); } return ''; } public function get_avatar_url(): string{ // if backend user logged in - try to get their avatar first, before trying to get customer url if($this->backend_user_type){ if($this->agent) return $this->agent->get_avatar_url(); if($this->wp_user) return get_avatar_url($this->wp_user->user_email); } if($this->customer) return $this->customer->get_avatar_url(); if($this->wp_user) return get_avatar_url($this->wp_user->user_email); return ''; } public function get_display_name(){ if($this->backend_user_type){ if($this->agent) return $this->agent->full_name; if($this->wp_user) return $this->wp_user->display_name; } if($this->customer) return $this->customer->full_name; if($this->wp_user) return $this->wp_user->display_name; } public function get_user_type_label(){ $labels = [ LATEPOINT_USER_TYPE_ADMIN => __('Administrator', 'latepoint'), LATEPOINT_USER_TYPE_AGENT => __('Agent', 'latepoint'), LATEPOINT_USER_TYPE_CUSTOM => __('Custom', 'latepoint') ]; if($this->backend_user_type){ if($this->backend_user_type == LATEPOINT_USER_TYPE_CUSTOM){ if($this->roles && ($this->roles[0] instanceof Role)){ return $this->roles[0]->name; }else{ return $labels[$this->backend_user_type]; } }else{ return $labels[$this->backend_user_type]; } }elseif($this->customer){ return __('Customer', 'latepoint'); } } public static function get_backend_user_types(){ $backend_user_types = [LATEPOINT_USER_TYPE_ADMIN, LATEPOINT_USER_TYPE_AGENT, LATEPOINT_USER_TYPE_CUSTOM]; /** * Get array of user levels that can access backend * * @since 4.7.0 * @hook latepoint_get_backend_user_types * * @param {array} $backend_user_types array of user levels (strings) that can access backend * @returns {array} The filtered array of user types */ return apply_filters('latepoint_get_backend_user_types', $backend_user_types); } public function has_backend_access(): bool{ return in_array($this->backend_user_type, self::get_backend_user_types()); } public static function allowed_props(): array{ return []; } }